Skip to content

Commit

Permalink
Implement most IWeakReference[Source] boilerplate
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanlabelle committed Mar 29, 2024
1 parent 4332d52 commit 5ce046b
Show file tree
Hide file tree
Showing 21 changed files with 562 additions and 6 deletions.
6 changes: 5 additions & 1 deletion swiftwinrt/Resources/CWinRT/CppInteropWorkaround.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ struct StaticWCharArray_512 {
#define IInspectableVtbl IInspectableVtbl_Workaround
#define IMarshal IMarshal_Workaround
#define IMarshalVtbl IMarshalVtbl_Workaround
#define IWeakReference IWeakReference_Workaround
#define IWeakReferenceVtbl IWeakReferenceVtbl_Workaround
#define IWeakReferenceSource IWeakReferenceSource_Workaround
#define IWeakReferenceSourceVtbl IWeakReferenceSourceVtbl_Workaround
#define IActivationFactory IActivationFactory_Workaround
#define IActivationFactoryVtbl IActivationFactoryVtbl_Workaround

Expand Down Expand Up @@ -291,4 +295,4 @@ typedef struct IActivationFactoryVtbl
interface IActivationFactory
{
CONST_VTBL struct IActivationFactoryVtbl *lpVtbl;
};
};
8 changes: 8 additions & 0 deletions swiftwinrt/Resources/Support/CppInteropWorkaround.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ public typealias C_IMarshal = C_BINDINGS_MODULE.IMarshal_Workaround
public typealias C_IMarshalVtbl = C_BINDINGS_MODULE.IMarshalVtbl_Workaround
public typealias C_IActivationFactory = C_BINDINGS_MODULE.IActivationFactory_Workaround
public typealias C_IActivationFactoryVtbl = C_BINDINGS_MODULE.IActivationFactoryVtbl_Workaround
public typealias C_IWeakReference = C_BINDINGS_MODULE.IWeakReference_Workaround
public typealias C_IWeakReferenceVtbl = C_BINDINGS_MODULE.IWeakReferenceVtbl_Workaround
public typealias C_IWeakReferenceSource = C_BINDINGS_MODULE.IWeakReferenceSource_Workaround
public typealias C_IWeakReferenceSourceVtbl = C_BINDINGS_MODULE.IWeakReferenceSourceVtbl_Workaround
internal let CoCreateInstance = C_BINDINGS_MODULE.CoCreateInstance_Workaround
internal let UuidFromStringA = C_BINDINGS_MODULE.UuidFromStringA_Workaround
internal let RoActivateInstance = C_BINDINGS_MODULE.RoActivateInstance_Workaround
Expand All @@ -30,6 +34,10 @@ public typealias C_IMarshal = C_BINDINGS_MODULE.IMarshal
public typealias C_IMarshalVtbl = C_BINDINGS_MODULE.IMarshalVtbl
public typealias C_IActivationFactory = C_BINDINGS_MODULE.IActivationFactory
public typealias C_IActivationFactoryVtbl = C_BINDINGS_MODULE.IActivationFactoryVtbl
public typealias C_IWeakReference = C_BINDINGS_MODULE.IWeakReference
public typealias C_IWeakReferenceVtbl = C_BINDINGS_MODULE.IWeakReferenceVtbl
public typealias C_IWeakReferenceSource = C_BINDINGS_MODULE.IWeakReferenceSource
public typealias C_IWeakReferenceSourceVtbl = C_BINDINGS_MODULE.IWeakReferenceSourceVtbl
internal let CoCreateInstance = C_BINDINGS_MODULE.CoCreateInstance
internal let UuidFromStringA = C_BINDINGS_MODULE.UuidFromStringA
internal let RoActivateInstance = C_BINDINGS_MODULE.RoActivateInstance
Expand Down
77 changes: 77 additions & 0 deletions swiftwinrt/Resources/Support/IWeakReference.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import C_BINDINGS_MODULE

internal let IID_IWeakReference: IID = IID(Data1: 0x00000037, Data2: 0x0000, Data3: 0x0000, Data4: (0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)) // 00000037-0000-0000-C000-000000000046

fileprivate extension IUnknownRef {
func copyTo(_ riid: REFIID?, _ ppvObj: UnsafeMutablePointer<LPVOID?>?) -> HRESULT {
self.borrow.pointee.lpVtbl.pointee.QueryInterface(self.borrow, riid, ppvObj)
}
}

func makeWeakReference(_ object: AnyObject, _ result: UnsafeMutablePointer<UnsafeMutableRawPointer?>) throws {
let wrapper = WeakReferenceWrapper(WeakReference(object))
try wrapper.toABI {
_ = $0.pointee.lpVtbl.pointee.AddRef($0)
result.pointee = UnsafeMutableRawPointer($0)
}
}

fileprivate class WeakReference {
public weak var target: AnyObject?
public init(_ target: AnyObject) {
self.target = target
}
}

fileprivate class WeakReferenceWrapper: WinRTAbiBridgeWrapper<IWeakReferenceBridge> {
init(_ weakReference: WeakReference){
super.init(IWeakReferenceBridge.makeAbi(), weakReference)
}
}

fileprivate enum IWeakReferenceBridge: AbiBridge {
typealias CABI = C_IWeakReference
typealias SwiftProjection = WeakReference

static func makeAbi() -> C_IWeakReference {
return C_IWeakReference(lpVtbl: &IWeakReferenceVTable)
}

static func from(abi: ComPtr<C_IWeakReference>?) -> WeakReference? {
fatalError("Not needed")
}
}

fileprivate var IWeakReferenceVTable: C_IWeakReferenceVtbl = .init(
QueryInterface: { pUnk, riid, ppvObject in
guard let pUnk, let riid, let ppvObject else { return E_INVALIDARG }
switch riid.pointee {
case IID_IWeakReference:
_ = pUnk.pointee.lpVtbl.pointee.AddRef(pUnk)
return S_OK
default:
guard let obj = WeakReferenceWrapper.tryUnwrapFromBase(raw: pUnk) else { return E_NOINTERFACE }
fatalError("\(#function): Not implemented: something like obj.QueryInterface")
}
},
AddRef: { WeakReferenceWrapper.addRef($0) },
Release: { WeakReferenceWrapper.release($0) },
Resolve: { Resolve($0, $1, $2) }
)

fileprivate func Resolve(
_ this: UnsafeMutablePointer<C_IWeakReference>?,
_ iid: UnsafePointer<GUID_Workaround>?,
_ inspectable: UnsafeMutablePointer<UnsafeMutablePointer<C_IInspectable>?>?) -> HRESULT {
guard let weakReference = WeakReferenceWrapper.tryUnwrapFrom(abi: ComPtr(this)) else { return E_FAIL }
guard let iid, let inspectable else { return E_INVALIDARG }
if let target = weakReference.target {
_ = target
_ = iid
fatalError("\(#function): Not implemented: something like target.QueryInterface")
}
else {
inspectable.pointee = nil
return S_OK
}
}
68 changes: 68 additions & 0 deletions swiftwinrt/Resources/Support/IWeakReferenceSource.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import C_BINDINGS_MODULE

internal let IID_IWeakReferenceSource: IID = IID(Data1: 0x00000038, Data2: 0x0000, Data3: 0x0000, Data4: (0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)) // 00000038-0000-0000-C000-000000000046

fileprivate extension IUnknownRef {
func copyTo(_ riid: REFIID?, _ ppvObj: UnsafeMutablePointer<LPVOID?>?) -> HRESULT {
self.borrow.pointee.lpVtbl.pointee.QueryInterface(self.borrow, riid, ppvObj)
}
}

func makeWeakReferenceSource(_ object: AnyObject, _ result: UnsafeMutablePointer<UnsafeMutableRawPointer?>) throws {
let wrapper = WeakReferenceSourceWrapper(object)
try wrapper.toABI {
_ = $0.pointee.lpVtbl.pointee.AddRef($0)
result.pointee = UnsafeMutableRawPointer($0)
}
}

fileprivate class WeakReferenceSourceWrapper: WinRTAbiBridgeWrapper<IWeakReferenceSourceBridge> {
init(_ object: AnyObject){
super.init(IWeakReferenceSourceBridge.makeAbi(), object)
}
}

fileprivate enum IWeakReferenceSourceBridge: AbiBridge {
typealias CABI = C_IWeakReferenceSource
typealias SwiftProjection = AnyObject

static func makeAbi() -> C_IWeakReferenceSource {
return C_IWeakReferenceSource(lpVtbl: &IWeakReferenceSourceVTable)
}

static func from(abi: ComPtr<C_IWeakReferenceSource>?) -> AnyObject? {
fatalError("Not needed")
}
}

fileprivate var IWeakReferenceSourceVTable: C_IWeakReferenceSourceVtbl = .init(
QueryInterface: { pUnk, riid, ppvObject in
guard let pUnk, let riid, let ppvObject else { return E_INVALIDARG }
switch riid.pointee {
case IID_IWeakReferenceSource:
_ = pUnk.pointee.lpVtbl.pointee.AddRef(pUnk)
return S_OK
default:
guard let obj = WeakReferenceSourceWrapper.tryUnwrapFromBase(raw: pUnk) else { return E_NOINTERFACE }
fatalError("\(#function): Not implemented: something like obj.QueryInterface")
}
},
AddRef: { WeakReferenceSourceWrapper.addRef($0) },
Release: { WeakReferenceSourceWrapper.release($0) },
GetWeakReference: { GetWeakReference($0, $1) }
)

fileprivate func GetWeakReference(
_ this: UnsafeMutablePointer<C_IWeakReferenceSource>?,
_ weakReference: UnsafeMutablePointer<UnsafeMutablePointer<C_IWeakReference>?>?) -> HRESULT {
guard let object = WeakReferenceSourceWrapper.tryUnwrapFrom(abi: ComPtr(this)) else { return E_FAIL }
guard let weakReference else { return E_INVALIDARG }
do {
var rawWeakReference: UnsafeMutableRawPointer? = nil
try makeWeakReference(object, &rawWeakReference)
weakReference.pointee = rawWeakReference?.bindMemory(to: C_IWeakReference.self, capacity: 1)
return S_OK
} catch {
return E_FAIL
}
}
7 changes: 4 additions & 3 deletions swiftwinrt/Resources/Support/WinRTWrapperBase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,12 @@ open class WinRTWrapperBase<CInterface, Prototype> {

fileprivate static func queryInterfaceBase(_ pUnk: UnsafeMutablePointer<CInterface>, _ riid: UnsafePointer<SUPPORT_MODULE.IID>, _ result: UnsafeMutablePointer<UnsafeMutableRawPointer?>) -> HRESULT {
guard let instance = tryUnwrapFromBase(raw: pUnk) else { return E_FAIL }
do
{
do {
switch riid.pointee {
case IID_IMarshal:
try makeMarshaler(IUnknownRef(ComPtr(pUnk)), result)
case IID_IWeakReferenceSource:
try makeWeakReferenceSource(instance as AnyObject, result)
default:
guard let customQI = instance as? CustomQueryInterface,
let iUnknownRef = customQI.queryInterface(riid.pointee) else { return E_NOINTERFACE }
Expand Down Expand Up @@ -252,4 +253,4 @@ public class ReferenceWrapperBase<I: ReferenceBridge>: WinRTAbiBridgeWrapper<I>
public static func tryUnwrapFrom(raw pUnk: UnsafeMutableRawPointer?) -> I.SwiftProjection? {
tryUnwrapFromBase(raw: pUnk)
}
}
}
3 changes: 3 additions & 0 deletions swiftwinrt/abi_writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ namespace swiftwinrt
w.c_mod = settings.get_c_module_name();
write_preamble(w, /* swift_code: */ false);
w.write(R"(#pragma once
#define CINTERFACE
#include <wtypesbase.h>
#include <minwindef.h>
#include <winnt.h>
Expand All @@ -313,6 +315,7 @@ namespace swiftwinrt
#pragma clang diagnostic ignored "-Wmicrosoft-enum-forward-reference"
#include "CppInteropWorkaround.h" // TODO(WIN-860): Remove workaround once C++ interop issues with WinSDK.GUID are fixed.
#include <weakreference.h>
#include "MemoryBuffer.h" // IMemoryBufferByteAccess (C definition)
#include "robuffer.h" // IBufferByteAccess (C definition)
)");
Expand Down
2 changes: 2 additions & 0 deletions swiftwinrt/resources.rc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ RawTyped RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\RawTyped.swift"
Runtime+Swift RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\Runtime+Swift.swift"
Swift+Extensions RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\Swift+Extensions.swift"
TrustLevel+Swift RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\TrustLevel+Swift.swift"
IWeakReference RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\IWeakReference.swift"
IWeakReferenceSource RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\IWeakReferenceSource.swift"
WinRTWrapperBase RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\WinRTWrapperBase.swift"
WinSDK+Extensions RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\WinSDK+Extensions.swift"
PropertyValue RESOURCE_TYPE_SWIFT_SUPPORT_FILE "Resources\\Support\\PropertyValue.swift"
Expand Down
33 changes: 33 additions & 0 deletions tests/test_app/WeakReferenceTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import WinSDK
import XCTest
@_spi(WinRTInternal) import test_component
import Ctest_component
import Foundation

class WeakReferenceTests : XCTestCase {
class Target: IReferenceTarget {
func method() {}
}

public func testCreateAndResolve() throws {
let original = Target()
let weakReferencer = WeakReferencer(original)
let roundtrippedAny = try XCTUnwrap(weakReferencer.resolve())
let roundtripped = try XCTUnwrap(roundtrippedAny as? Target)
XCTAssertIdentical(roundtripped, original)
}

public func testNotStrong() throws {
var original: Target! = Target()
let weakReferencer = WeakReferencer(original)
original = nil
XCTAssertNil(try weakReferencer.resolve())
}
}

var weakReferenceTests: [XCTestCaseEntry] = [
testCase([
("testCreateAndResolve", WeakReferenceTests.testCreateAndResolve),
("testNotStrong", WeakReferenceTests.testNotStrong)
])
]
2 changes: 1 addition & 1 deletion tests/test_app/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ var tests: [XCTestCaseEntry] = [
("testUnicode", SwiftWinRTTests.testUnicode),
("testErrorInfo", SwiftWinRTTests.testErrorInfo),
])
] + valueBoxingTests + eventTests + collectionTests + aggregationTests + asyncTests + memoryManagementTests + bufferTests
] + valueBoxingTests + eventTests + collectionTests + aggregationTests + asyncTests + memoryManagementTests + bufferTests + weakReferenceTests

RoInitialize(RO_INIT_MULTITHREADED)
XCTMain(tests)
6 changes: 5 additions & 1 deletion tests/test_app/test_app.exe.manifest
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,9 @@
name="test_component.BufferTester"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1" />
<activatableClass
name="test_component.WeakReferencer"
threadingModel="both"
xmlns="urn:schemas-microsoft-com:winrt.v1" />
</file>
</assembly>
</assembly>
3 changes: 3 additions & 0 deletions tests/test_component/Sources/CWinRT/include/Ctest_component.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// WARNING: Please don't edit this file. It was generated by Swift/WinRT v0.0.1

#pragma once
#define CINTERFACE

#include <wtypesbase.h>
#include <minwindef.h>
#include <winnt.h>
Expand All @@ -23,6 +25,7 @@
#pragma clang diagnostic ignored "-Wmicrosoft-enum-forward-reference"

#include "CppInteropWorkaround.h" // TODO(WIN-860): Remove workaround once C++ interop issues with WinSDK.GUID are fixed.
#include <weakreference.h>
#include "MemoryBuffer.h" // IMemoryBufferByteAccess (C definition)
#include "robuffer.h" // IBufferByteAccess (C definition)
#include "Windows.AI.MachineLearning.h"
Expand Down
Loading

0 comments on commit 5ce046b

Please sign in to comment.