This backs C++17's std::pmr implementation.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=49954 Signed-off-by: Arkadiusz Hiler ahiler@codeweavers.com --- configure | 1 + configure.ac | 1 + dlls/msvcp140_1/Makefile.in | 9 +- dlls/msvcp140_1/msvcp140_1.c | 266 +++++++++++++++++++++++++++ dlls/msvcp140_1/msvcp140_1.spec | 14 +- dlls/msvcp140_1/tests/Makefile.in | 3 + dlls/msvcp140_1/tests/msvcp140_1.c | 277 +++++++++++++++++++++++++++++ 7 files changed, 563 insertions(+), 8 deletions(-) create mode 100644 dlls/msvcp140_1/msvcp140_1.c create mode 100644 dlls/msvcp140_1/tests/Makefile.in create mode 100644 dlls/msvcp140_1/tests/msvcp140_1.c
diff --git a/configure b/configure index a19bb0b9581..aedbac67fe4 100755 --- a/configure +++ b/configure @@ -20825,6 +20825,7 @@ wine_fn_config_makefile dlls/msvcp120_app enable_msvcp120_app wine_fn_config_makefile dlls/msvcp140 enable_msvcp140 wine_fn_config_makefile dlls/msvcp140/tests enable_tests wine_fn_config_makefile dlls/msvcp140_1 enable_msvcp140_1 +wine_fn_config_makefile dlls/msvcp140_1/tests enable_tests wine_fn_config_makefile dlls/msvcp60 enable_msvcp60 wine_fn_config_makefile dlls/msvcp60/tests enable_tests wine_fn_config_makefile dlls/msvcp70 enable_msvcp70 diff --git a/configure.ac b/configure.ac index eb08e8b0f17..edd7be66ec3 100644 --- a/configure.ac +++ b/configure.ac @@ -3500,6 +3500,7 @@ WINE_CONFIG_MAKEFILE(dlls/msvcp120_app) WINE_CONFIG_MAKEFILE(dlls/msvcp140) WINE_CONFIG_MAKEFILE(dlls/msvcp140/tests) WINE_CONFIG_MAKEFILE(dlls/msvcp140_1) +WINE_CONFIG_MAKEFILE(dlls/msvcp140_1/tests) WINE_CONFIG_MAKEFILE(dlls/msvcp60) WINE_CONFIG_MAKEFILE(dlls/msvcp60/tests) WINE_CONFIG_MAKEFILE(dlls/msvcp70) diff --git a/dlls/msvcp140_1/Makefile.in b/dlls/msvcp140_1/Makefile.in index 6582299d7d6..846bcdc855b 100644 --- a/dlls/msvcp140_1/Makefile.in +++ b/dlls/msvcp140_1/Makefile.in @@ -1 +1,8 @@ -MODULE = msvcp140_1.dll +MODULE = msvcp140_1.dll +PARENTSRC = ../msvcp90 + +EXTRADLLFLAGS = -mno-cygwin -fno-builtin + +C_SRCS = \ + type_info.c \ + msvcp140_1.c diff --git a/dlls/msvcp140_1/msvcp140_1.c b/dlls/msvcp140_1/msvcp140_1.c new file mode 100644 index 00000000000..344fee3dc53 --- /dev/null +++ b/dlls/msvcp140_1/msvcp140_1.c @@ -0,0 +1,266 @@ +/* + * Copyright 2021 Arkadiusz Hiler for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> +#include <stdbool.h> +#include <malloc.h> + +#include "windef.h" +#include "winbase.h" +#include "wine/debug.h" +#include "winnls.h" +#include "cxx.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvcp); + +#ifdef __i386__ +#define MAX_UNALIGNED_ALIGNMENT 8 +#else +#define MAX_UNALIGNED_ALIGNMENT 16 +#endif + +static HMODULE msvcp140; + +void init_type_info(void *base); +int CDECL _callnewh(size_t size); + +void (__cdecl *throw_bad_alloc)(void); + +/* non-static, needed by type_info */ +void* __cdecl MSVCRT_operator_new(size_t size) +{ + void *retval; + int freed; + + do + { + retval = malloc(size); + if (retval) + { + TRACE("(%Iu) returning %p\n", size, retval); + return retval; + } + freed = _callnewh(size); + } while (freed); + + TRACE("(%Iu) out of memory\n", size); + throw_bad_alloc(); + return NULL; +} + +void __cdecl MSVCRT_operator_delete(void *mem) +{ + free(mem); +} + +static void* __cdecl MSVCRT_operator_new_aligned(size_t size, size_t alignment) +{ + void *retval; + int freed; + + do + { + retval = _aligned_malloc(size, alignment); + if (retval) + { + TRACE("(%Iu) returning %p\n", size, retval); + return retval; + } + freed = _callnewh(size); + } while (freed); + + TRACE("(%Iu) out of memory\n", size); + throw_bad_alloc(); + return NULL; +} + +static void __cdecl MSVCRT_operator_delete_aligned(void *mem, size_t alignment) +{ + _aligned_free(mem); +} + +typedef struct { + const vtable_ptr *vtable; +} memory_resource; + +extern const vtable_ptr MSVCP_aligned_resource_vtable; +extern const vtable_ptr MSVCP_unaligned_resource_vtable; +extern const vtable_ptr MSVCP_null_resource_vtable; + +__ASM_BLOCK_BEGIN(vtables) + __ASM_VTABLE(base_memory_resource, + VTABLE_ADD_FUNC(nop_dtor)); + __ASM_VTABLE(aligned_resource, + VTABLE_ADD_FUNC(nop_dtor) + VTABLE_ADD_FUNC(aligned_do_allocate) + VTABLE_ADD_FUNC(aligned_do_deallocate) + VTABLE_ADD_FUNC(do_is_equal)); + __ASM_VTABLE(unaligned_resource, + VTABLE_ADD_FUNC(nop_dtor) + VTABLE_ADD_FUNC(unaligned_do_allocate) + VTABLE_ADD_FUNC(unaligned_do_deallocate) + VTABLE_ADD_FUNC(do_is_equal)); + __ASM_VTABLE(null_resource, + VTABLE_ADD_FUNC(nop_dtor) + VTABLE_ADD_FUNC(null_do_allocate) + VTABLE_ADD_FUNC(null_do_deallocate) + VTABLE_ADD_FUNC(do_is_equal)); +__ASM_BLOCK_END + +DEFINE_RTTI_DATA0(base_memory_resource, 0, ".?AVmemory_resource@pmr@std@@") +DEFINE_RTTI_DATA1(aligned_resource, 0, &base_memory_resource_rtti_base_descriptor, ".?AV_Aligned_new_delete_resource@pmr@std@@") +DEFINE_RTTI_DATA1(unaligned_resource, 0, &base_memory_resource_rtti_base_descriptor, ".?AV_Unligned_new_delete_resource@pmr@std@@") +DEFINE_RTTI_DATA1(null_resource, 0, &base_memory_resource_rtti_base_descriptor, ".?AV_null_resource@pmr@std@@") + +DEFINE_THISCALL_WRAPPER(nop_dtor, 4) +void __thiscall nop_dtor(void *this) +{ + /* nop */ +} + +DEFINE_THISCALL_WRAPPER(do_is_equal, 8) +bool __thiscall do_is_equal(memory_resource *this, memory_resource *other) +{ + return this == other; +} + +DEFINE_THISCALL_WRAPPER(aligned_do_allocate, 12) +void* __thiscall aligned_do_allocate(memory_resource *this, size_t bytes, size_t alignment) +{ + return MSVCRT_operator_new_aligned(bytes, alignment); +} + +DEFINE_THISCALL_WRAPPER(aligned_do_deallocate, 16) +void __thiscall aligned_do_deallocate(memory_resource *this, void *p, size_t bytes, size_t alignment) +{ + MSVCRT_operator_delete_aligned(p, alignment); +} + +DEFINE_THISCALL_WRAPPER(unaligned_do_allocate, 12) +void* __thiscall unaligned_do_allocate(memory_resource *this, size_t bytes, size_t alignment) +{ + if (alignment > MAX_UNALIGNED_ALIGNMENT) + throw_bad_alloc(); + + return MSVCRT_operator_new(bytes); +} + +DEFINE_THISCALL_WRAPPER(unaligned_do_deallocate, 16) +void __thiscall unaligned_do_deallocate(memory_resource *this, void *p, size_t bytes, size_t alignment) +{ + MSVCRT_operator_delete(p); +} + +DEFINE_THISCALL_WRAPPER(null_do_allocate, 12) +void* __thiscall null_do_allocate(memory_resource *this, size_t bytes, size_t alignment) +{ + throw_bad_alloc(); + return NULL; +} + +DEFINE_THISCALL_WRAPPER(null_do_deallocate, 16) +void __thiscall null_do_deallocate(memory_resource *this, void *p, size_t bytes, size_t alignment) +{ + /* nop */ +} + +static memory_resource impl_aligned_resource = { &MSVCP_aligned_resource_vtable }; +static memory_resource impl_unaligned_resource = { &MSVCP_unaligned_resource_vtable }; +static memory_resource impl_null_resource = { &MSVCP_null_resource_vtable }; + +memory_resource *default_unaligned_resource = &impl_unaligned_resource; +memory_resource *default_aligned_resource = &impl_aligned_resource; + +/* EXPORTS */ + +memory_resource* __cdecl _Aligned_new_delete_resource(void) +{ + return &impl_aligned_resource; +} + +memory_resource* __cdecl _Unaligned_new_delete_resource(void) +{ + return &impl_unaligned_resource; +} + +memory_resource* __cdecl _Aligned_get_default_resource(void) +{ + return default_aligned_resource; +} + +memory_resource* __cdecl _Aligned_set_default_resource(memory_resource *res) +{ + if (res == NULL) + res = &impl_aligned_resource; + + return InterlockedExchangePointer((void**)&default_aligned_resource, res); +} + +memory_resource* __cdecl _Unaligned_get_default_resource(void) +{ + return default_unaligned_resource; +} + +memory_resource* __cdecl _Unaligned_set_default_resource(memory_resource *res) +{ + if (res == NULL) + res = &impl_unaligned_resource; + + return InterlockedExchangePointer((void**)&default_unaligned_resource, res); +} + +memory_resource* __cdecl null_memory_resource(void) +{ + return &impl_null_resource; +} + +/* DLL INIT */ + +static void init_cxx_funcs(void) +{ + msvcp140 = LoadLibraryA("msvcp140.dll"); + if (!msvcp140) FIXME("Failed to load msvcp140.dll\n"); + throw_bad_alloc = (void*)GetProcAddress(msvcp140, "?_Xbad_alloc@std@@YAXXZ"); + if (!throw_bad_alloc) FIXME("Failed to get address of ?_Xbad_alloc@std@@YAXXZ\n"); +} + +static void init_memory_resource(void *base) +{ +#ifdef __x86_64__ + init_null_resource_rtti(base); + init_unaligned_resource_rtti(base); + init_aligned_resource_rtti(base); + init_base_memory_resource_rtti(base); +#endif +} + +BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved ) +{ + switch (reason) + { + case DLL_PROCESS_ATTACH: + init_cxx_funcs(); + init_type_info(inst); + init_memory_resource(inst); + break; + case DLL_PROCESS_DETACH: + FreeLibrary(msvcp140); + break; + } + return TRUE; +} diff --git a/dlls/msvcp140_1/msvcp140_1.spec b/dlls/msvcp140_1/msvcp140_1.spec index bfe82ab00da..e4446f4f4c2 100644 --- a/dlls/msvcp140_1/msvcp140_1.spec +++ b/dlls/msvcp140_1/msvcp140_1.spec @@ -1,7 +1,7 @@ -@ stub _Aligned_get_default_resource -@ stub _Aligned_new_delete_resource -@ stub _Aligned_set_default_resource -@ stub _Unaligned_get_default_resource -@ stub _Unaligned_new_delete_resource -@ stub _Unaligned_set_default_resource -@ stub null_memory_resource +@ cdecl _Aligned_get_default_resource() +@ cdecl _Aligned_new_delete_resource() +@ cdecl _Aligned_set_default_resource(ptr) +@ cdecl _Unaligned_get_default_resource() +@ cdecl _Unaligned_new_delete_resource() +@ cdecl _Unaligned_set_default_resource(ptr) +@ cdecl null_memory_resource() diff --git a/dlls/msvcp140_1/tests/Makefile.in b/dlls/msvcp140_1/tests/Makefile.in new file mode 100644 index 00000000000..523a2016d3a --- /dev/null +++ b/dlls/msvcp140_1/tests/Makefile.in @@ -0,0 +1,3 @@ +TESTDLL = msvcp140_1.dll + +C_SRCS = msvcp140_1.c diff --git a/dlls/msvcp140_1/tests/msvcp140_1.c b/dlls/msvcp140_1/tests/msvcp140_1.c new file mode 100644 index 00000000000..a32dd364fe8 --- /dev/null +++ b/dlls/msvcp140_1/tests/msvcp140_1.c @@ -0,0 +1,277 @@ +/* + * Copyright 2021 Arkadiusz Hiler for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <errno.h> +#include <stdio.h> + +#include "windef.h" +#include "winbase.h" +#include "winnls.h" + +#include "wine/test.h" +#include "wine/exception.h" +#include "winbase.h" + +typedef unsigned char MSVCP_bool; + +#undef __thiscall +#ifdef __i386__ +#define __thiscall __stdcall +#else +#define __thiscall __cdecl +#endif + +#ifdef __i386__ +#define MAX_UNALIGNED_ALIGNMENT 8 +#else +#define MAX_UNALIGNED_ALIGNMENT 16 +#endif + +/* Emulate __thiscall */ +#ifdef __i386__ + +#include "pshpack1.h" +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 */ +}; +#include "poppack.h" + +static void* (WINAPI *call_thiscall_func1)(void *func, void *this); +static void* (WINAPI *call_thiscall_func2)(void *func, void *this, const void *a); +static void* (WINAPI *call_thiscall_func3)(void *func, void *this, const void *a, const void *b); +static void* (WINAPI *call_thiscall_func4)(void *func, void *this, const void *a, const void *b, const void *c); + +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; + call_thiscall_func2 = (void*)thunk; + call_thiscall_func3 = (void*)thunk; + call_thiscall_func4 = (void*)thunk; +} + +#define call_func1(func,_this) call_thiscall_func1(func,_this) +#define call_func2(func,_this,a) call_thiscall_func2(func,_this,(const void*)(a)) +#define call_func3(func,_this,a,b) call_thiscall_func3(func,_this,(const void*)(a),(const void*)(b)) +#define call_func4(func,_this,a,b,c) call_thiscall_func4(func,_this,(const void*)(a),(const void*)(b), (const void*)(c)) + +#else + +#define init_thiscall_thunk() +#define call_func1(func,_this) func(_this) +#define call_func2(func,_this,a) func(_this,a) +#define call_func3(func,_this,a,b) func(_this,a,b) +#define call_func4(func,_this,a,b,c) func(_this,a,b,c) + +#endif /* __i386__ */ + +struct memory_resource_vtbl; + +typedef struct { + struct memory_resource_vtbl *vtbl; +} memory_resource; + +struct memory_resource_vtbl +{ + void (__thiscall *dtor)(void *this); + void* (__thiscall *do_allocate)(memory_resource *this, size_t bytes, size_t alignment); + void (__thiscall *do_deallocate)(memory_resource *this, void *p, size_t bytes, size_t alignment); + MSVCP_bool (__thiscall *do_is_equal)(memory_resource *this, memory_resource *other); +}; + +static HMODULE msvcp; +static memory_resource* (__cdecl *p__Aligned_new_delete_resource)(void); +static memory_resource* (__cdecl *p__Unaligned_new_delete_resource)(void); +static memory_resource* (__cdecl *p__Aligned_get_default_resource)(void); +static memory_resource* (__cdecl *p__Unaligned_get_default_resource)(void); +static memory_resource* (__cdecl *p__Aligned_set_default_resource)(memory_resource* resource); +static memory_resource* (__cdecl *p__Unaligned_set_default_resource)(memory_resource* resource); +static memory_resource* (__cdecl *p_null_memory_resource)(void); + +static HMODULE ucrtbase; +static void* (__cdecl *p_malloc)(size_t size); +static void (__cdecl *p_free)(void *ptr); +static void* (__cdecl *p__aligned_malloc)(size_t size, size_t alignment); +static void (__cdecl *p__aligned_free)(void *ptr); + +#define SETNOFAIL(lib,x,y) x = (void*)GetProcAddress(lib,y) +#define SET(lib,x,y) do { SETNOFAIL(lib,x,y); ok(x != NULL, "Export '%s' not found\n", y); } while(0) + +static BOOL init(void) +{ + msvcp = LoadLibraryA("msvcp140_1.dll"); + if(!msvcp) + { + win_skip("msvcp140_1.dll not installed\n"); + return FALSE; + } + + ucrtbase = LoadLibraryA("ucrtbase.dll"); + if(!ucrtbase) + { + win_skip("ucrtbase.dll not installed\n"); + FreeLibrary(msvcp); + return FALSE; + } + + SET(msvcp, p__Aligned_new_delete_resource, "_Aligned_new_delete_resource"); + SET(msvcp, p__Unaligned_new_delete_resource, "_Unaligned_new_delete_resource"); + SET(msvcp, p_null_memory_resource, "null_memory_resource"); + SET(msvcp, p__Aligned_get_default_resource, "_Aligned_get_default_resource"); + SET(msvcp, p__Unaligned_get_default_resource, "_Unaligned_get_default_resource"); + SET(msvcp, p__Aligned_set_default_resource, "_Aligned_set_default_resource"); + SET(msvcp, p__Unaligned_set_default_resource, "_Unaligned_set_default_resource"); + + SET(ucrtbase, p__aligned_malloc, "_aligned_malloc"); + SET(ucrtbase, p__aligned_free, "_aligned_free"); + SET(ucrtbase, p_malloc, "malloc"); + SET(ucrtbase, p_free, "free"); + + init_thiscall_thunk(); + + return TRUE; +} + +static void test__Aligned_new_delete_resource(void) +{ + void *ptr; + memory_resource *resource = p__Aligned_new_delete_resource(); + ok(resource != NULL, "Failed to get aligned new delete memory resource.\n"); + + /* calling dtor should be harmless nop */ + call_func1(resource->vtbl->dtor, resource); + + ptr = call_func3(resource->vtbl->do_allocate, resource, 140, 32); + ok(ptr != NULL, "Failed to allocate memory using memory resource.\n"); + call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, 32); + + /* up to the alignment of 16 it is using non-aligned new/delete */ + ptr = call_func3(resource->vtbl->do_allocate, resource, 140, 16); + ok(ptr != NULL, "Failed to allocate memory using memory resource.\n"); + p_free(ptr); /* aligned delete, crashes with non-aligned */ + + ptr = p_malloc(140); + ok(ptr != NULL, "Failed to allocate memory using malloc.\n"); + call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, 8); + + /* past the alignment of 16 it is using aligned new/delete */ + ptr = call_func3(resource->vtbl->do_allocate, resource, 140, 32); + ok(ptr != NULL, "Failed to allocate memory using memory resource.\n"); + p__aligned_free(ptr); /* aligned delete, crashes with non-aligned */ + + ptr = p__aligned_malloc(140, 32); + ok(ptr != NULL, "Failed to allocate memory using _aligned_malloc.\n"); + call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, 32); + + ok((MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource), "Expected resource to be equal to itself.\n"); + ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, NULL), "Expected resource to not be equal to NULL.\n"); + ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource+1), "Expected resource to not be equal to a random pointer.\n"); +} + +static void test__Unaligned_new_delete_resource(void) +{ + void *ptr; + memory_resource *resource = p__Unaligned_new_delete_resource(); + ok(resource != NULL, "Failed to get unaligned new delete memory resource.\n"); + + /* calling dtor should be a harmless nop */ + call_func1(resource->vtbl->dtor, resource); + + ptr = call_func3(resource->vtbl->do_allocate, resource, 140, MAX_UNALIGNED_ALIGNMENT); + ok(ptr != NULL, "Failed to allocate memory using memory resource.\n"); + call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, MAX_UNALIGNED_ALIGNMENT); + + /* up to the alignment of 16 it is using non-aligned new/delete */ + ptr = call_func3(resource->vtbl->do_allocate, resource, 140, 8); + ok(ptr != NULL, "Failed to allocate memory using memory resource.\n"); + p_free(ptr); /* aligned delete */ + + ptr = p_malloc(140); + ok(ptr != NULL, "Failed to allocate memory using malloc.\n"); + call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, 2); + + /* alignment past MAX_UNALIGNED_ALIGNMENT results in bad alloc exception */ + /* ptr = call_func3(resource->vtbl->do_allocate, resource, 140, MAX_UNALIGNED_ALIGNMENT*2); */ + + ok((MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource), "Expected resource to be equal to itself.\n"); + ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, NULL), "Expected resource to not be equal to NULL.\n"); + ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource+1), "Expected resource to not be equal to a random pointer.\n"); +} + +static void test_null_memory_resource(void) +{ + memory_resource *resource = p_null_memory_resource(); + ok(resource != NULL, "Failed to get null memory resource.\n"); + + /* should result in bad alloc exception */ + /* call_func3(resource->vtbl->do_allocate, resource, 140, 8); */ + + /* harmless nop */ + call_func4(resource->vtbl->do_deallocate, resource, (void*)(INT_PTR)-1, 140, 2); + + ok((MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource), "Expected resource to be equal to itself.\n"); + ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, NULL), "Expected resource to not be equal to NULL.\n"); + ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource+1), "Expected resource to not be equal to a random pointer.\n"); +} + +static void test_get_set_defult_resource(memory_resource *(__cdecl *new_delete_resource)(void), + memory_resource *(__cdecl *get_default_resource)(void), + memory_resource *(__cdecl *set_default_resource)(memory_resource *resource)) +{ + memory_resource *new_resource = new_delete_resource(); + memory_resource *default_resource = get_default_resource(); + ok(default_resource == new_resource, "Expected the default memory resource to be equal new/delete one.\n"); + + default_resource = set_default_resource((void*)0xdeadbeef); + ok(default_resource == new_resource, "Expected that setting default resource would return the old one.\n"); + + default_resource = get_default_resource(); + ok(default_resource == (void*)0xdeadbeef, "Expected that setting reasource would take effect.\n"); + + default_resource = set_default_resource(NULL); + ok(default_resource == (void*)0xdeadbeef, "Expected that setting default resource would return the old one.\n"); + + default_resource = get_default_resource(); + ok(default_resource == new_resource, "Expected that setting default resource to NULL would reset the value.\n"); +} + +START_TEST(msvcp140_1) +{ + if (!init()) return; + + test__Aligned_new_delete_resource(); + test__Unaligned_new_delete_resource(); + + test_null_memory_resource(); + + test_get_set_defult_resource(p__Aligned_new_delete_resource, p__Aligned_get_default_resource, p__Aligned_set_default_resource); + test_get_set_defult_resource(p__Unaligned_new_delete_resource, p__Unaligned_get_default_resource, p__Unaligned_set_default_resource); + + FreeLibrary(msvcp); + FreeLibrary(ucrtbase); +}