The `Allocate` method has two variants:
* Allocate(size_t): Performs a normal allocation, but throws a COMException on OOM. * Allocate(ptrdiff_t offset, size_t size): Allocates a block of size `size`, and initializes a control block for an `IWeakReference` at `offset`. The weak reference resolves to the allocated object, which can finally be freed by calling `ControlBlock::ReleaseTarget`.
The IWeakReference implementation seems to be rather odd, which I have tried to demonstrate (and test) in the unit tests:
* `ReleaseTarget` only frees the object if the strong ref count is _below_ 0. * `IWeakReference::Resolve` will _not_ set the output pointer to `NULL` if the weak reference is no longer alive.
This MR implements these functions sans exception support.
-- v2: vccorlib140: Implement Platform::Details::{Allocate(ptrdiff_t, size_t), ControlBlock::ReleaseTarget}. vccorlib140: Add stub for Platform::Details::{Allocate(ptrdiff_t, size_t), ControlBlock::ReleaseTarget}.
From: Vibhav Pant vibhavp@gmail.com
--- dlls/vccorlib140/tests/vccorlib.c | 23 +++++++++++++++++++++++ dlls/vccorlib140/vccorlib.c | 20 ++++++++++++++++++++ dlls/vccorlib140/vccorlib140.spec | 8 ++++---- 3 files changed, 47 insertions(+), 4 deletions(-)
diff --git a/dlls/vccorlib140/tests/vccorlib.c b/dlls/vccorlib140/tests/vccorlib.c index 890472f0b46..56f054a0596 100644 --- a/dlls/vccorlib140/tests/vccorlib.c +++ b/dlls/vccorlib140/tests/vccorlib.c @@ -60,6 +60,8 @@ static HRESULT (__cdecl *pInitializeData)(int); static void (__cdecl *pUninitializeData)(int); static HRESULT (WINAPI *pGetActivationFactoryByPCWSTR)(const WCHAR *, const GUID *, void **); static HRESULT (WINAPI *pGetIidsFn)(UINT32, UINT32 *, const GUID *, GUID **); +static void *(__cdecl *pAllocate)(size_t); +static void (__cdecl *pFree)(void *);
static BOOL init(void) { @@ -88,16 +90,22 @@ static BOOL init(void) pGetActivationFactoryByPCWSTR = (void *)GetProcAddress(hmod, "?GetActivationFactoryByPCWSTR@@YAJPEAXAEAVGuid@Platform@@PEAPEAX@Z"); pGetIidsFn = (void *)GetProcAddress(hmod, "?GetIidsFn@@YAJHPEAKPEBU__s_GUID@@PEAPEAVGuid@Platform@@@Z"); + pAllocate = (void *)GetProcAddress(hmod, "?Allocate@Heap@Details@Platform@@SAPEAX_K@Z"); + pFree = (void *)GetProcAddress(hmod, "?Free@Heap@Details@Platform@@SAXPEAX@Z"); } else { pGetActivationFactoryByPCWSTR = (void *)GetProcAddress(hmod, "?GetActivationFactoryByPCWSTR@@YGJPAXAAVGuid@Platform@@PAPAX@Z"); pGetIidsFn = (void *)GetProcAddress(hmod, "?GetIidsFn@@YGJHPAKPBU__s_GUID@@PAPAVGuid@Platform@@@Z"); + pAllocate = (void *)GetProcAddress(hmod, "?Allocate@Heap@Details@Platform@@SAPAXI@Z"); + pFree = (void *)GetProcAddress(hmod, "?Free@Heap@Details@Platform@@SAXPAX@Z"); } #endif ok(pGetActivationFactoryByPCWSTR != NULL, "GetActivationFactoryByPCWSTR not available\n"); ok(pGetIidsFn != NULL, "GetIidsFn not available\n"); + ok(pAllocate != NULL, "Allocate not available\n"); + ok(pFree != NULL, "Free not available\n");
return TRUE; } @@ -293,6 +301,20 @@ static void test_GetIidsFn(void) CoTaskMemFree(guids_dest); }
+static void test_Allocate(void) +{ + void *addr; + + addr = pAllocate(0); + ok(!!addr, "got addr %p\n", addr); + pFree(addr); + + addr = pAllocate(sizeof(void *)); + ok(!!addr, "got addr %p\n", addr); + pFree(addr); + pFree(NULL); +} + START_TEST(vccorlib) { if(!init()) @@ -301,4 +323,5 @@ START_TEST(vccorlib) test_InitializeData(); test_GetActivationFactoryByPCWSTR(); test_GetIidsFn(); + test_Allocate(); } diff --git a/dlls/vccorlib140/vccorlib.c b/dlls/vccorlib140/vccorlib.c index 23310ec542e..6bae9660302 100644 --- a/dlls/vccorlib140/vccorlib.c +++ b/dlls/vccorlib140/vccorlib.c @@ -73,3 +73,23 @@ HRESULT WINAPI GetIidsFn(unsigned int count, unsigned int *copied, const GUID *s
return S_OK; } + +void *__cdecl Allocate(size_t size) +{ + void *addr; + + TRACE("(%Iu)\n", size); + + addr = malloc(size); + /* TODO: Throw a COMException on allocation failure. */ + if (!addr) + FIXME("allocation failure\n"); + return addr; +} + +void __cdecl Free(void *addr) +{ + TRACE("(%p)\n", addr); + + free(addr); +} diff --git a/dlls/vccorlib140/vccorlib140.spec b/dlls/vccorlib140/vccorlib140.spec index 503b2771ee7..e5dcd9b5a8b 100644 --- a/dlls/vccorlib140/vccorlib140.spec +++ b/dlls/vccorlib140/vccorlib140.spec @@ -66,8 +66,8 @@ @ stub -arch=i386 ?FlushFactoryCache@@YGXXZ @ stub -arch=arm ?FlushFactoryCache@@YAXXZ @ stub -arch=win64 ?FlushFactoryCache@@YAXXZ -@ stub -arch=win32 ?Free@Heap@Details@Platform@@SAXPAX@Z -@ stub -arch=win64 ?Free@Heap@Details@Platform@@SAXPEAX@Z +@ cdecl -arch=win32 ?Free@Heap@Details@Platform@@SAXPAX@Z(ptr) Free +@ cdecl -arch=win64 ?Free@Heap@Details@Platform@@SAXPEAX@Z(ptr) Free @ stub -arch=win32 ?FreeException@Heap@Details@Platform@@SAXPAX@Z @ stub -arch=win64 ?FreeException@Heap@Details@Platform@@SAXPEAX@Z @ stub -arch=i386 ?GetActivationFactory@Details@Platform@@YGJPAVModuleBase@1WRL@Microsoft@@PAUHSTRING__@@PAPAUIActivationFactory@@@Z @@ -501,10 +501,10 @@ @ stub -arch=win64 ?AlignedFree@Heap@Details@Platform@@SAXPEAX@Z @ stub -arch=win32 ?AlignedFreeException@Heap@Details@Platform@@SAXPAX@Z @ stub -arch=win64 ?AlignedFreeException@Heap@Details@Platform@@SAXPEAX@Z -@ stub -arch=win32 ?Allocate@Heap@Details@Platform@@SAPAXI@Z +@ cdecl -arch=win32 ?Allocate@Heap@Details@Platform@@SAPAXI@Z(long) Allocate @ stub -arch=win64 ?Allocate@Heap@Details@Platform@@SAPEAX_K0@Z @ stub -arch=win32 ?Allocate@Heap@Details@Platform@@SAPAXII@Z -@ stub -arch=win64 ?Allocate@Heap@Details@Platform@@SAPEAX_K@Z +@ cdecl -arch=win64 ?Allocate@Heap@Details@Platform@@SAPEAX_K@Z(long) Allocate @ stub -arch=win32 ?AllocateException@Heap@Details@Platform@@SAPAXI@Z @ stub -arch=win64 ?AllocateException@Heap@Details@Platform@@SAPEAX_K0@Z @ stub -arch=win32 ?AllocateException@Heap@Details@Platform@@SAPAXII@Z
From: Vibhav Pant vibhavp@gmail.com
--- dlls/vccorlib140/tests/vccorlib.c | 293 ++++++++++++++++++++++++++++++ dlls/vccorlib140/vccorlib.c | 14 ++ dlls/vccorlib140/vccorlib140.spec | 8 +- 3 files changed, 311 insertions(+), 4 deletions(-)
diff --git a/dlls/vccorlib140/tests/vccorlib.c b/dlls/vccorlib140/tests/vccorlib.c index 56f054a0596..b0391957a30 100644 --- a/dlls/vccorlib140/tests/vccorlib.c +++ b/dlls/vccorlib140/tests/vccorlib.c @@ -21,9 +21,12 @@
#define COBJMACROS
+#include <stdbool.h> + #include "initguid.h" #include "activation.h" #include "objbase.h" +#include "weakreference.h" #include "wine/test.h"
#define DEFINE_EXPECT(func) \ @@ -51,6 +54,50 @@ called_ ## func = 0; \ }while(0)
+#undef __thiscall +#ifdef __i386__ + +#pragma pack(push,1) +struct thiscall_thunk +{ + BYTE pop_eax; /* popl %eax (ret addr) */ + BYTE pop_edx; /* popl %edx (func) */ + BYTE pop_ecx; /* popl %ecx (this) */ + BYTE push_eax; /* pushl %eax */ + WORD jmp_edx; /* jmp *%edx */ +}; +#pragma pack( pop ) + +static ULONG_PTR (WINAPI *call_thiscall_func1)(void *func, void *this); + +static void init_thiscall_thunk(void) +{ + struct thiscall_thunk *thunk = VirtualAlloc(NULL, sizeof(*thunk), MEM_COMMIT, PAGE_EXECUTE_READWRITE); + thunk->pop_eax = 0x58; /* popl %eax */ + thunk->pop_edx = 0x5a; /* popl %edx */ + thunk->pop_ecx = 0x59; /* popl %ecx */ + thunk->push_eax = 0x50; /* pushl %eax */ + thunk->jmp_edx = 0xe2ff; /* jmp *%edx */ + call_thiscall_func1 = (void *)thunk; +} + +#define __thiscall __stdcall +#define call_func1(func,_this) call_thiscall_func1(func,_this) + +#else + +#define init_thiscall_thunk() +#define __thiscall __cdecl +#define call_func1(func,_this) func(_this) + +#endif /* __i386__ */ + +#ifdef __i386__ +#define __thiscall __stdcall +#else +#define __thiscall __cdecl +#endif + DEFINE_EXPECT(PreInitialize); DEFINE_EXPECT(PostInitialize); DEFINE_EXPECT(PreUninitialize); @@ -62,6 +109,8 @@ static HRESULT (WINAPI *pGetActivationFactoryByPCWSTR)(const WCHAR *, const GUID static HRESULT (WINAPI *pGetIidsFn)(UINT32, UINT32 *, const GUID *, GUID **); static void *(__cdecl *pAllocate)(size_t); static void (__cdecl *pFree)(void *); +static void *(__cdecl *pAllocateWithWeakRef)(ptrdiff_t, size_t); +static void (__thiscall *pReleaseTarget)(void *);
static BOOL init(void) { @@ -92,6 +141,8 @@ static BOOL init(void) pGetIidsFn = (void *)GetProcAddress(hmod, "?GetIidsFn@@YAJHPEAKPEBU__s_GUID@@PEAPEAVGuid@Platform@@@Z"); pAllocate = (void *)GetProcAddress(hmod, "?Allocate@Heap@Details@Platform@@SAPEAX_K@Z"); pFree = (void *)GetProcAddress(hmod, "?Free@Heap@Details@Platform@@SAXPEAX@Z"); + pAllocateWithWeakRef = (void *)GetProcAddress(hmod, "?Allocate@Heap@Details@Platform@@SAPEAX_K0@Z"); + pReleaseTarget = (void *)GetProcAddress(hmod, "?ReleaseTarget@ControlBlock@Details@Platform@@AEAAXXZ"); } else { @@ -100,12 +151,18 @@ static BOOL init(void) pGetIidsFn = (void *)GetProcAddress(hmod, "?GetIidsFn@@YGJHPAKPBU__s_GUID@@PAPAVGuid@Platform@@@Z"); pAllocate = (void *)GetProcAddress(hmod, "?Allocate@Heap@Details@Platform@@SAPAXI@Z"); pFree = (void *)GetProcAddress(hmod, "?Free@Heap@Details@Platform@@SAXPAX@Z"); + pAllocateWithWeakRef = (void *)GetProcAddress(hmod, "?Allocate@Heap@Details@Platform@@SAPAXII@Z"); + pReleaseTarget = (void *)GetProcAddress(hmod, "?ReleaseTarget@ControlBlock@Details@Platform@@AAEXXZ"); } #endif ok(pGetActivationFactoryByPCWSTR != NULL, "GetActivationFactoryByPCWSTR not available\n"); ok(pGetIidsFn != NULL, "GetIidsFn not available\n"); ok(pAllocate != NULL, "Allocate not available\n"); ok(pFree != NULL, "Free not available\n"); + ok(pAllocateWithWeakRef != NULL, "AllocateWithWeakRef not available\n"); + ok(pReleaseTarget != NULL, "ReleaseTarget not available\n"); + + init_thiscall_thunk();
return TRUE; } @@ -315,6 +372,240 @@ static void test_Allocate(void) pFree(NULL); }
+#define test_refcount(a, b) test_refcount_(__LINE__, (a), (b)) +static void test_refcount_(int line, void *obj, LONG val) +{ + LONG count; + + IUnknown_AddRef((IUnknown *)obj); + count = IUnknown_Release((IUnknown *)obj); + ok_(__FILE__, line)(count == val, "got refcount %lu != %lu\n", count, val); +} + +struct weakref +{ + IWeakReference IWeakReference_iface; + LONG ref_weak; + LONG ref_strong; + IUnknown *object; + bool is_inline; + UINT16 unknown; +#ifdef _WIN32 + char _padding[4]; +#endif +}; + +struct unknown_impl +{ + IUnknown IUnknown_iface; + ULONG strong_ref_free_val; /* Should be a negative value */ + struct weakref *weakref; +}; + +static struct unknown_impl *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct unknown_impl, IUnknown_iface); +} + +static HRESULT WINAPI unknown_QueryInterface(IUnknown *iface, const GUID *iid, void **out) +{ + struct unknown_impl *impl = impl_from_IUnknown(iface); + + if (winetest_debug > 1) + trace("(%p, %s, %p)\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IAgileObject)) + { + *out = &impl->IUnknown_iface; + IUnknown_AddRef(&impl->IUnknown_iface); + return S_OK; + } + + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI unknown_AddRef(IUnknown *iface) +{ + struct unknown_impl *impl = impl_from_IUnknown(iface); + + return InterlockedIncrement(&impl->weakref->ref_strong); +} + +static ULONG WINAPI unknown_Release(IUnknown *iface) +{ + struct unknown_impl *impl = impl_from_IUnknown(iface); + LONG ref = InterlockedDecrement(&impl->weakref->ref_strong); + + if (!ref) + { + struct weakref *weak = impl->weakref; + BOOL is_inline = weak->is_inline; + IUnknown *out = NULL; + LONG count; + HRESULT hr; + + /* The object will only be freed when the strong refcount is < 0. */ + call_func1(pReleaseTarget, weak); + hr = IWeakReference_QueryInterface(&weak->IWeakReference_iface, &IID_IWeakReference, (void **)&out); + ok(hr == S_OK, "got hr %#lx\n", hr); + test_refcount(out, 3); + IUnknown_Release(out); + + /* Resolve on native seems to *not* set out to NULL if the weak reference is no longer there. */ + out = (IUnknown *)0xdeadbeef; + hr = IWeakReference_Resolve(&weak->IWeakReference_iface, &IID_IAgileObject, (IInspectable **)&out); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(out == (IUnknown *)0xdeadbeef, "got out %p\n", out); + + impl->weakref->ref_strong = impl->strong_ref_free_val; + /* Frees this object. */ + call_func1(pReleaseTarget, weak); + if (is_inline) + { + /* For inline allocations, ReleaseTarget should do nothing. */ + out = NULL; + hr = IWeakReference_QueryInterface(&weak->IWeakReference_iface, &IID_IWeakReference, (void **)&out); + ok(hr == S_OK, "got hr %#lx\n", hr); + test_refcount(out, 3); + IUnknown_Release(out); + } + + /* ReleaseTarget can still be called after the object has been freed. */ + call_func1(pReleaseTarget, weak); + count = IWeakReference_Release(&weak->IWeakReference_iface); + ok(count == 1, "got count %lu\n", count); + } + return ref; +} + + +static const IUnknownVtbl unknown_impl_vtbl = +{ + unknown_QueryInterface, + unknown_AddRef, + unknown_Release, +}; + +/* The maximum size for inline allocations. */ +#ifdef _WIN64 +#define INLINE_MAX 128 +#else +#define INLINE_MAX 64 +#endif +/* Make sure that unknown_impl can be allocated inline. */ +C_ASSERT(sizeof(struct unknown_impl) <= INLINE_MAX); + +static void test_AllocateWithWeakRef_inline(void) +{ + struct unknown_impl *object; + IWeakReference *weakref; + IUnknown *out; + ULONG count; + HRESULT hr; + + /* Test inline allocation. */ + object = pAllocateWithWeakRef(offsetof(struct unknown_impl, weakref), sizeof(struct unknown_impl)); + todo_wine ok(object != NULL, "got object %p\n", object); + if (!object) + { + skip("AllocateWithWeakRef returned NULL\n"); + return; + } + + object->strong_ref_free_val = -1; + ok(object->weakref != NULL, "got weakref %p\n", object->weakref); + object->IUnknown_iface.lpVtbl = &unknown_impl_vtbl; + weakref = &object->weakref->IWeakReference_iface; + test_refcount(weakref, 1); + ok(object->weakref->is_inline, "got is_inline %d\n", object->weakref->is_inline); + ok(object->weakref->ref_strong == 1, "got ref_strong %lu\n", object->weakref->ref_strong); + ok(object->weakref->object == &object->IUnknown_iface, "got object %p != %p\n", object->weakref->object, + &object->IUnknown_iface); + ok(object->weakref->unknown == 0, "got unknown %d\n", object->weakref->unknown); + /* The object is allocate within the weakref. */ + ok((char *)object->weakref == ((char *)object - sizeof(struct weakref)), "got %p != %p\n", object->weakref, + (char *)object - sizeof(struct weakref)); + + hr = IWeakReference_Resolve(weakref, &IID_IAgileObject, (IInspectable **)&out); + ok(hr == S_OK, "got hr %#lx\n", hr); + test_refcount(&object->IUnknown_iface, 2); + IUnknown_Release(out); + + /* Doesn't do anything if the object is still available. */ + call_func1(pReleaseTarget, object->weakref); + hr = IWeakReference_Resolve(weakref, &IID_IAgileObject, (IInspectable **)&out); + ok(hr == S_OK, "got hr %#lx\n", hr); + test_refcount(&object->IUnknown_iface, 2); + IUnknown_Release(out); + + count = IWeakReference_AddRef(weakref); + ok(count == 2, "got count %lu\n", count); + + count = IUnknown_Release(&object->IUnknown_iface); + ok(count == 0, "got count %lu\n", count); + test_refcount(weakref, 1); + out = (IUnknown *)0xdeadbeef; + hr = IWeakReference_Resolve(weakref, &IID_IAgileObject, (IInspectable **)&out); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(out == (IUnknown *)0xdeadbeef, "got out %p\n", out); + count = IWeakReference_Release(weakref); + ok(count == 0, "got count %lu\n", count); +} + +static void test_AllocateWithWeakRef(void) +{ + struct unknown_impl *object; + IWeakReference *weakref; + IUnknown *out; + ULONG count; + HRESULT hr; + + /* Test non-inline allocation. */ + object = pAllocateWithWeakRef(offsetof(struct unknown_impl, weakref), INLINE_MAX + 1); + todo_wine ok(object != NULL, "got object %p\n", object); + if (!object) + { + skip("AllocateWithWeakRef returned NULL\n"); + return; + } + + object->strong_ref_free_val = -100; + ok(object->weakref != NULL, "got weakref %p\n", object->weakref); + object->IUnknown_iface.lpVtbl = &unknown_impl_vtbl; + weakref = &object->weakref->IWeakReference_iface; + test_refcount(weakref, 1); + ok(!object->weakref->is_inline, "got is_inline %d\n", object->weakref->is_inline); + ok(object->weakref->ref_strong == 1, "got ref_strong %lu\n", object->weakref->ref_strong); + ok(object->weakref->object == &object->IUnknown_iface, "got object %p != %p\n", object->weakref->object, + &object->IUnknown_iface); + ok(object->weakref->unknown == 0, "got unknown %d\n", object->weakref->unknown); + + hr = IWeakReference_Resolve(weakref, &IID_IAgileObject, (IInspectable **)&out); + ok(hr == S_OK, "got hr %#lx\n", hr); + test_refcount(&object->IUnknown_iface, 2); + IUnknown_Release(out); + + call_func1(pReleaseTarget, object->weakref); + hr = IWeakReference_Resolve(weakref, &IID_IAgileObject, (IInspectable **)&out); + ok(hr == S_OK, "got hr %#lx\n", hr); + test_refcount(&object->IUnknown_iface, 2); + IUnknown_Release(out); + + count = IWeakReference_AddRef(weakref); + ok(count == 2, "got count %lu\n", count); + + count = IUnknown_Release(&object->IUnknown_iface); + ok(count == 0, "got count %lu\n", count); + test_refcount(weakref, 1); + out = (IUnknown *)0xdeadbeef; + hr = IWeakReference_Resolve(weakref, &IID_IAgileObject, (IInspectable **)&out); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(out == (IUnknown *)0xdeadbeef, "got out %p\n", out); + count = IWeakReference_Release(weakref); + ok(count == 0, "got count %lu\n", count); +} + START_TEST(vccorlib) { if(!init()) @@ -324,4 +615,6 @@ START_TEST(vccorlib) test_GetActivationFactoryByPCWSTR(); test_GetIidsFn(); test_Allocate(); + test_AllocateWithWeakRef_inline(); + test_AllocateWithWeakRef(); } diff --git a/dlls/vccorlib140/vccorlib.c b/dlls/vccorlib140/vccorlib.c index 6bae9660302..a169d210ba8 100644 --- a/dlls/vccorlib140/vccorlib.c +++ b/dlls/vccorlib140/vccorlib.c @@ -19,6 +19,7 @@
#include "roapi.h" #include "winstring.h" +#include "wine/asm.h" #include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(vccorlib); @@ -93,3 +94,16 @@ void __cdecl Free(void *addr)
free(addr); } + +void *__cdecl AllocateWithWeakRef(ptrdiff_t offset, size_t size) +{ + FIXME("(%Iu, %Iu) stub\n", offset, size); + + return NULL; +} + +DEFINE_THISCALL_WRAPPER(control_block_ReleaseTarget, 4) +void __thiscall control_block_ReleaseTarget(void *weakref) +{ + FIXME("(%p) stub\n", weakref); +} diff --git a/dlls/vccorlib140/vccorlib140.spec b/dlls/vccorlib140/vccorlib140.spec index e5dcd9b5a8b..c54caf844b4 100644 --- a/dlls/vccorlib140/vccorlib140.spec +++ b/dlls/vccorlib140/vccorlib140.spec @@ -205,8 +205,8 @@ @ stub -arch=i386 ?ReleaseInContextImpl@Details@Platform@@YGJPAUIUnknown@@0@Z @ stub -arch=arm ?ReleaseInContextImpl@Details@Platform@@YAJPAUIUnknown@@0@Z @ stub -arch=win64 ?ReleaseInContextImpl@Details@Platform@@YAJPEAUIUnknown@@0@Z -@ stub -arch=win32 ?ReleaseTarget@ControlBlock@Details@Platform@@AAEXXZ -@ stub -arch=win64 ?ReleaseTarget@ControlBlock@Details@Platform@@AEAAXXZ +@ thiscall -arch=win32 ?ReleaseTarget@ControlBlock@Details@Platform@@AAEXXZ(ptr) control_block_ReleaseTarget +@ cdecl -arch=win64 ?ReleaseTarget@ControlBlock@Details@Platform@@AEAAXXZ(ptr) control_block_ReleaseTarget @ stub -arch=i386 ?ResolveWeakReference@Details@Platform@@YGP$AAVObject@2@ABU_GUID@@PAPAU__abi_IUnknown@@@Z @ stub -arch=arm ?ResolveWeakReference@Details@Platform@@YAP$AAVObject@2@ABU_GUID@@PAPAU__abi_IUnknown@@@Z @ stub -arch=win64 ?ResolveWeakReference@Details@Platform@@YAPE$AAVObject@2@AEBU_GUID@@PEAPEAU__abi_IUnknown@@@Z @@ -502,8 +502,8 @@ @ stub -arch=win32 ?AlignedFreeException@Heap@Details@Platform@@SAXPAX@Z @ stub -arch=win64 ?AlignedFreeException@Heap@Details@Platform@@SAXPEAX@Z @ cdecl -arch=win32 ?Allocate@Heap@Details@Platform@@SAPAXI@Z(long) Allocate -@ stub -arch=win64 ?Allocate@Heap@Details@Platform@@SAPEAX_K0@Z -@ stub -arch=win32 ?Allocate@Heap@Details@Platform@@SAPAXII@Z +@ cdecl -arch=win64 ?Allocate@Heap@Details@Platform@@SAPEAX_K0@Z(long long) AllocateWithWeakRef +@ cdecl -arch=win32 ?Allocate@Heap@Details@Platform@@SAPAXII@Z(long long) AllocateWithWeakRef @ cdecl -arch=win64 ?Allocate@Heap@Details@Platform@@SAPEAX_K@Z(long) Allocate @ stub -arch=win32 ?AllocateException@Heap@Details@Platform@@SAPAXI@Z @ stub -arch=win64 ?AllocateException@Heap@Details@Platform@@SAPEAX_K0@Z
From: Vibhav Pant vibhavp@gmail.com
--- dlls/vccorlib140/tests/vccorlib.c | 14 ++-- dlls/vccorlib140/vccorlib.c | 126 +++++++++++++++++++++++++++++- 2 files changed, 129 insertions(+), 11 deletions(-)
diff --git a/dlls/vccorlib140/tests/vccorlib.c b/dlls/vccorlib140/tests/vccorlib.c index b0391957a30..9de2b0ef7ca 100644 --- a/dlls/vccorlib140/tests/vccorlib.c +++ b/dlls/vccorlib140/tests/vccorlib.c @@ -382,7 +382,7 @@ static void test_refcount_(int line, void *obj, LONG val) ok_(__FILE__, line)(count == val, "got refcount %lu != %lu\n", count, val); }
-struct weakref +struct control_block { IWeakReference IWeakReference_iface; LONG ref_weak; @@ -399,7 +399,7 @@ struct unknown_impl { IUnknown IUnknown_iface; ULONG strong_ref_free_val; /* Should be a negative value */ - struct weakref *weakref; + struct control_block *weakref; };
static struct unknown_impl *impl_from_IUnknown(IUnknown *iface) @@ -439,7 +439,7 @@ static ULONG WINAPI unknown_Release(IUnknown *iface)
if (!ref) { - struct weakref *weak = impl->weakref; + struct control_block *weak = impl->weakref; BOOL is_inline = weak->is_inline; IUnknown *out = NULL; LONG count; @@ -506,7 +506,7 @@ static void test_AllocateWithWeakRef_inline(void)
/* Test inline allocation. */ object = pAllocateWithWeakRef(offsetof(struct unknown_impl, weakref), sizeof(struct unknown_impl)); - todo_wine ok(object != NULL, "got object %p\n", object); + ok(object != NULL, "got object %p\n", object); if (!object) { skip("AllocateWithWeakRef returned NULL\n"); @@ -524,8 +524,8 @@ static void test_AllocateWithWeakRef_inline(void) &object->IUnknown_iface); ok(object->weakref->unknown == 0, "got unknown %d\n", object->weakref->unknown); /* The object is allocate within the weakref. */ - ok((char *)object->weakref == ((char *)object - sizeof(struct weakref)), "got %p != %p\n", object->weakref, - (char *)object - sizeof(struct weakref)); + ok((char *)object->weakref == ((char *)object - sizeof(struct control_block)), "got %p != %p\n", object->weakref, + (char *)object - sizeof(struct control_block));
hr = IWeakReference_Resolve(weakref, &IID_IAgileObject, (IInspectable **)&out); ok(hr == S_OK, "got hr %#lx\n", hr); @@ -563,7 +563,7 @@ static void test_AllocateWithWeakRef(void)
/* Test non-inline allocation. */ object = pAllocateWithWeakRef(offsetof(struct unknown_impl, weakref), INLINE_MAX + 1); - todo_wine ok(object != NULL, "got object %p\n", object); + ok(object != NULL, "got object %p\n", object); if (!object) { skip("AllocateWithWeakRef returned NULL\n"); diff --git a/dlls/vccorlib140/vccorlib.c b/dlls/vccorlib140/vccorlib.c index a169d210ba8..e225e7ce5a7 100644 --- a/dlls/vccorlib140/vccorlib.c +++ b/dlls/vccorlib140/vccorlib.c @@ -17,7 +17,13 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#define COBJMACROS + +#include <stdbool.h> + +#include "initguid.h" #include "roapi.h" +#include "weakreference.h" #include "winstring.h" #include "wine/asm.h" #include "wine/debug.h" @@ -95,15 +101,127 @@ void __cdecl Free(void *addr) free(addr); }
+struct control_block +{ + IWeakReference IWeakReference_iface; + LONG ref_weak; + LONG ref_strong; + IUnknown *object; + bool is_inline; + UINT16 unknown; +#ifdef _WIN32 + char _padding[4]; +#endif +}; + +static inline struct control_block *impl_from_IWeakReference(IWeakReference *iface) +{ + return CONTAINING_RECORD(iface, struct control_block, IWeakReference_iface); +} + +static HRESULT WINAPI control_block_QueryInterface(IWeakReference *iface, const GUID *iid, void **out) +{ + struct control_block *impl = impl_from_IWeakReference(iface); + + TRACE("(%p, %s, %p)", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IWeakReference)) + { + IWeakReference_AddRef((*out = &impl->IWeakReference_iface)); + return S_OK; + } + + *out = NULL; + ERR("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI control_block_AddRef(IWeakReference *iface) +{ + struct control_block *impl = impl_from_IWeakReference(iface); + + TRACE("(%p)\n", iface); + + return InterlockedIncrement(&impl->ref_weak); +} + +static ULONG WINAPI control_block_Release(IWeakReference *iface) +{ + struct control_block *impl = impl_from_IWeakReference(iface); + ULONG ref = InterlockedDecrement(&impl->ref_weak); + + TRACE("(%p)\n", iface); + + if (!ref) Free(impl); + return ref; +} + +static HRESULT WINAPI control_block_Resolve(IWeakReference *iface, const GUID *iid, IInspectable **out) +{ + struct control_block *impl = impl_from_IWeakReference(iface); + HRESULT hr; + LONG ref; + + TRACE("(%p, %s, %p)\n", iface, debugstr_guid(iid), out); + + do + { + ref = ReadNoFence(&impl->ref_strong); + if (ref <= 0) + return S_OK; + } while (ref != InterlockedCompareExchange(&impl->ref_strong, ref + 1, ref)); + + hr = IUnknown_QueryInterface(impl->object, iid, (void **)out); + IUnknown_Release(impl->object); + return hr; +} + +static const IWeakReferenceVtbl control_block_vtbl = +{ + control_block_QueryInterface, + control_block_AddRef, + control_block_Release, + control_block_Resolve, +}; + void *__cdecl AllocateWithWeakRef(ptrdiff_t offset, size_t size) { - FIXME("(%Iu, %Iu) stub\n", offset, size); + const size_t inline_max = sizeof(void *) == 8 ? 128 : 64; + struct control_block *weakref; + void *object; + + TRACE("(%Iu, %Iu)\n", offset, size); + + if (size > inline_max) + { + weakref = Allocate(sizeof(*weakref)); + object = Allocate(size); + weakref->is_inline = FALSE; + } + else /* Perform an inline allocation */ + { + weakref = Allocate(sizeof(*weakref) + size); + object = (char *)weakref + sizeof(*weakref); + weakref->is_inline = TRUE; + } + + *(struct control_block **)((char *)object + offset) = weakref; + weakref->IWeakReference_iface.lpVtbl = &control_block_vtbl; + weakref->object = object; + weakref->ref_strong = weakref->ref_weak = 1; + weakref->unknown = 0;
- return NULL; + return weakref->object; }
DEFINE_THISCALL_WRAPPER(control_block_ReleaseTarget, 4) -void __thiscall control_block_ReleaseTarget(void *weakref) +void __thiscall control_block_ReleaseTarget(struct control_block *weakref) { - FIXME("(%p) stub\n", weakref); + void *object; + + TRACE("(%p)\n", weakref); + + if (weakref->is_inline || ReadNoFence(&weakref->ref_strong) >= 0) return; + if ((object = InterlockedCompareExchangePointer((void *)&weakref->object, NULL, weakref->object))) + Free(object); }
This merge request was approved by Piotr Caban.
Alexandre Julliard (@julliard) commented about dlls/vccorlib140/vccorlib140.spec:
@ stub -arch=i386 ?ReleaseInContextImpl@Details@Platform@@YGJPAUIUnknown@@0@Z @ stub -arch=arm ?ReleaseInContextImpl@Details@Platform@@YAJPAUIUnknown@@0@Z @ stub -arch=win64 ?ReleaseInContextImpl@Details@Platform@@YAJPEAUIUnknown@@0@Z -@ stub -arch=win32 ?ReleaseTarget@ControlBlock@Details@Platform@@AAEXXZ -@ stub -arch=win64 ?ReleaseTarget@ControlBlock@Details@Platform@@AEAAXXZ +@ thiscall -arch=win32 ?ReleaseTarget@ControlBlock@Details@Platform@@AAEXXZ(ptr) control_block_ReleaseTarget +@ cdecl -arch=win64 ?ReleaseTarget@ControlBlock@Details@Platform@@AEAAXXZ(ptr) control_block_ReleaseTarget
thiscall only makes sense for i386, the symbol must be named differently for arm.