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