From: Piotr Caban piotr@codeweavers.com
Signed-off-by: Arkadiusz Hiler ahiler@codeweavers.com --- dlls/msvcp90/cxx.h | 32 ++++++++++++++++++++++++++++++++ dlls/msvcp90/exception.c | 32 ++------------------------------ 2 files changed, 34 insertions(+), 30 deletions(-)
diff --git a/dlls/msvcp90/cxx.h b/dlls/msvcp90/cxx.h index 02735a3c168..5052f433875 100644 --- a/dlls/msvcp90/cxx.h +++ b/dlls/msvcp90/cxx.h @@ -449,3 +449,35 @@ typedef struct } cxx_exception_type;
#endif + +#define CREATE_TYPE_INFO_VTABLE\ + static void MSVCP_type_info_dtor(type_info * _this) \ +{ \ + free(_this->name); \ +} \ +\ +DEFINE_THISCALL_WRAPPER(MSVCP_type_info_vector_dtor,8) \ +void * __thiscall MSVCP_type_info_vector_dtor(type_info * _this, unsigned int flags) \ +{ \ + if (flags & 2) \ + { \ + /* we have an array, with the number of elements stored before the first object */ \ + INT_PTR i, *ptr = (INT_PTR *)_this - 1; \ +\ + for (i = *ptr - 1; i >= 0; i--) MSVCP_type_info_dtor(_this + i); \ + free(ptr); \ + } \ + else \ + { \ + MSVCP_type_info_dtor(_this); \ + if (flags & 1) free(_this); \ + } \ + return _this; \ +} \ +\ +DEFINE_RTTI_DATA0( type_info, 0, ".?AVtype_info@@" ) \ +\ +__ASM_BLOCK_BEGIN(type_info_vtables) \ + __ASM_VTABLE(type_info, \ + VTABLE_ADD_FUNC(MSVCP_type_info_vector_dtor)); \ +__ASM_BLOCK_END diff --git a/dlls/msvcp90/exception.c b/dlls/msvcp90/exception.c index 8ea4e132a0f..496dd915608 100644 --- a/dlls/msvcp90/exception.c +++ b/dlls/msvcp90/exception.c @@ -26,6 +26,8 @@
WINE_DEFAULT_DEBUG_CHANNEL(msvcp);
+CREATE_TYPE_INFO_VTABLE + #define CLASS_IS_SIMPLE_TYPE 1 #define CLASS_HAS_VIRTUAL_BASE_CLASS 4
@@ -65,34 +67,6 @@ extern const vtable_ptr MSVCP_bad_cast_vtable; /* ??_7range_error@std@@6B@ */ extern const vtable_ptr MSVCP_range_error_vtable;
-static void MSVCP_type_info_dtor(type_info * _this) -{ - free(_this->name); -} - -/* Unexported */ -DEFINE_THISCALL_WRAPPER(MSVCP_type_info_vector_dtor,8) -void * __thiscall MSVCP_type_info_vector_dtor(type_info * _this, unsigned int flags) -{ - TRACE("(%p %x)\n", _this, flags); - if (flags & 2) - { - /* we have an array, with the number of elements stored before the first object */ - INT_PTR i, *ptr = (INT_PTR *)_this - 1; - - for (i = *ptr - 1; i >= 0; i--) MSVCP_type_info_dtor(_this + i); - MSVCRT_operator_delete(ptr); - } - else - { - MSVCP_type_info_dtor(_this); - if (flags & 1) MSVCRT_operator_delete(_this); - } - return _this; -} - -DEFINE_RTTI_DATA0( type_info, 0, ".?AVtype_info@@" ) - /* ??0exception@@QAE@ABQBD@Z */ /* ??0exception@@QEAA@AEBQEBD@Z */ DEFINE_THISCALL_WRAPPER(MSVCP_exception_ctor,8) @@ -1022,8 +996,6 @@ void __cdecl __ExceptionPtrDestroy(exception_ptr *ep) #endif
__ASM_BLOCK_BEGIN(exception_vtables) - __ASM_VTABLE(type_info, - VTABLE_ADD_FUNC(MSVCP_type_info_vector_dtor)); EXCEPTION_VTABLE(exception, VTABLE_ADD_FUNC(MSVCP_exception_vector_dtor) VTABLE_ADD_FUNC(MSVCP_exception_what));
Suggested by Piotr Caban.
Signed-off-by: Arkadiusz Hiler ahiler@codeweavers.com --- dlls/msvcp90/cxx.h | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/dlls/msvcp90/cxx.h b/dlls/msvcp90/cxx.h index 5052f433875..0c5b83f93f6 100644 --- a/dlls/msvcp90/cxx.h +++ b/dlls/msvcp90/cxx.h @@ -46,7 +46,7 @@
#ifndef __x86_64__
-#define DEFINE_RTTI_DATA(name, off, base_classes_no, cl1, cl2, cl3, cl4, cl5, cl6, cl7, cl8, cl9, mangled_name) \ +#define DEFINE_RTTI_BASE(name, base_classes_no, mangled_name) \ static type_info name ## _type_info = { \ &MSVCP_type_info_vtable, \ NULL, \ @@ -58,7 +58,10 @@ static const rtti_base_descriptor name ## _rtti_base_descriptor = { \ base_classes_no, \ { 0, -1, 0}, \ 64 \ -}; \ +}; + +#define DEFINE_RTTI_DATA(name, off, base_classes_no, cl1, cl2, cl3, cl4, cl5, cl6, cl7, cl8, cl9, mangled_name) \ + DEFINE_RTTI_BASE(name, base_classes_no, mangled_name) \ \ static const rtti_base_array name ## _rtti_base_array = { \ { \ @@ -122,7 +125,7 @@ static const cxx_exception_type type ## _cxx_type = { \
#else
-#define DEFINE_RTTI_DATA(name, off, base_classes_no, cl1, cl2, cl3, cl4, cl5, cl6, cl7, cl8, cl9, mangled_name) \ +#define __DEFINE_RTTI_BASE(name, base_classes_no, mangled_name) \ static type_info name ## _type_info = { \ &MSVCP_type_info_vtable, \ NULL, \ @@ -134,7 +137,18 @@ static rtti_base_descriptor name ## _rtti_base_descriptor = { \ base_classes_no, \ { 0, -1, 0}, \ 64 \ -}; \ +}; + +#define DEFINE_RTTI_BASE(name, base_classes_no, mangled_name) \ + __DEFINE_RTTI_BASE(name, base_classes_no, mangled_name) \ + \ + static void init_ ## name ## _rtti(char *base) \ + { \ + name ## _rtti_base_descriptor.type_descriptor = (char*)&name ## _type_info - base; \ + } + +#define DEFINE_RTTI_DATA(name, off, base_classes_no, cl1, cl2, cl3, cl4, cl5, cl6, cl7, cl8, cl9, mangled_name) \ + __DEFINE_RTTI_BASE(name, base_classes_no, mangled_name) \ \ static rtti_base_array name ## _rtti_base_array = { \ { \
Signed-off-by: Piotr Caban piotr@codeweavers.com
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 | 7 +- dlls/msvcp140_1/msvcp140_1.c | 283 +++++++++++++++++++++++++++++ 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, 578 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 4665a6f5876..9eee07f8876 100755 --- a/configure +++ b/configure @@ -20827,6 +20827,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 2217330d60e..2f5a6987a13 100644 --- a/configure.ac +++ b/configure.ac @@ -3501,6 +3501,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..b0c8c58a5ba 100644 --- a/dlls/msvcp140_1/Makefile.in +++ b/dlls/msvcp140_1/Makefile.in @@ -1 +1,6 @@ -MODULE = msvcp140_1.dll +MODULE = msvcp140_1.dll +PARENTSRC = ../msvcp90 + +EXTRADLLFLAGS = -mno-cygwin -fno-builtin + +C_SRCS = 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..a220f9d0d22 --- /dev/null +++ b/dlls/msvcp140_1/msvcp140_1.c @@ -0,0 +1,283 @@ +/* + * 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" + +#define NEW_ALIGNMENT (2*sizeof(void*)) + +WINE_DEFAULT_DEBUG_CHANNEL(msvcp); + +CREATE_TYPE_INFO_VTABLE + +static HMODULE msvcp140; + +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(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_BASE(base_memory_resource, 0, ".?AVmemory_resource@pmr@std@@") +DEFINE_RTTI_BASE(_Identity_equal_resource, 0, ".?AV_Identity_equal_resource@pmr@std@@") +DEFINE_RTTI_DATA2(aligned_resource, 0, &_Identity_equal_resource_rtti_base_descriptor, + &base_memory_resource_rtti_base_descriptor, ".?AV_Aligned_new_delete_resource_impl@pmr@std@@") +DEFINE_RTTI_DATA2(unaligned_resource, 0, &_Identity_equal_resource_rtti_base_descriptor, + &base_memory_resource_rtti_base_descriptor, ".?AV_Unaligned_new_delete_resource_impl@pmr@std@@") +DEFINE_RTTI_DATA2(null_resource, 0, &_Identity_equal_resource_rtti_base_descriptor, + &base_memory_resource_rtti_base_descriptor, + ".?AV_Null_resource@?1??null_memory_resource@@YAPAVmemory_resource@pmr@std@@XZ") + +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) +{ + if (alignment > NEW_ALIGNMENT) + return MSVCRT_operator_new_aligned(bytes, alignment); + else + return MSVCRT_operator_new(bytes); +} + +DEFINE_THISCALL_WRAPPER(aligned_do_deallocate, 16) +void __thiscall aligned_do_deallocate(memory_resource *this, void *p, size_t bytes, size_t alignment) +{ + if (alignment > NEW_ALIGNMENT) + MSVCRT_operator_delete_aligned(p, alignment); + else + MSVCRT_operator_delete(p); +} + +DEFINE_THISCALL_WRAPPER(unaligned_do_allocate, 12) +void* __thiscall unaligned_do_allocate(memory_resource *this, size_t bytes, size_t alignment) +{ + if (alignment > NEW_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 */ +} + +memory_resource *default_resource = NULL; + +/* EXPORTS */ + +memory_resource* __cdecl _Aligned_new_delete_resource(void) +{ + static memory_resource impl = { &MSVCP_aligned_resource_vtable }; + return &impl; +} + +memory_resource* __cdecl _Unaligned_new_delete_resource(void) +{ + static memory_resource impl = { &MSVCP_unaligned_resource_vtable }; + return &impl; +} + +memory_resource* __cdecl _Aligned_get_default_resource(void) +{ + if (default_resource) return default_resource; + return _Aligned_new_delete_resource(); +} + +memory_resource* __cdecl _Aligned_set_default_resource(memory_resource *res) +{ + memory_resource *ret = InterlockedExchangePointer((void**)&default_resource, res); + if (!ret) ret = _Aligned_new_delete_resource(); + return ret; +} + +memory_resource* __cdecl _Unaligned_get_default_resource(void) +{ + if (default_resource) return default_resource; + return _Unaligned_new_delete_resource(); +} + +memory_resource* __cdecl _Unaligned_set_default_resource(memory_resource *res) +{ + memory_resource *ret = InterlockedExchangePointer((void**)&default_resource, res); + if (!ret) ret = _Unaligned_new_delete_resource(); + return ret; +} + +memory_resource* __cdecl null_memory_resource(void) +{ + static memory_resource impl = { &MSVCP_null_resource_vtable }; + return &impl; +} + +/* DLL INIT */ + +static BOOL init_cxx_funcs(void) +{ + msvcp140 = LoadLibraryA("msvcp140.dll"); + if (!msvcp140) + { + FIXME("Failed to load msvcp140.dll\n"); + return FALSE; + } + + 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"); + FreeLibrary(msvcp140); + return FALSE; + } + + return TRUE; +} + +static void init_rtti(void *base) +{ +#ifdef __x86_64__ + init_type_info_rtti(base); + init_base_memory_resource_rtti(base); + init__Identity_equal_resource_rtti(base); + init_aligned_resource_rtti(base); + init_unaligned_resource_rtti(base); + init_null_resource_rtti(base); +#endif +} + +BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) +{ + switch (reason) + { + case DLL_PROCESS_ATTACH: + if (!init_cxx_funcs()) return FALSE; + init_rtti(inst); + break; + case DLL_PROCESS_DETACH: + if (reserved) break; + 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..644108bc9d1 --- /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 + +#define NEW_ALIGNMENT (2*sizeof(void*)) + +/* 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"); + + ptr = call_func3(resource->vtbl->do_allocate, resource, 140, 64); + ok(ptr != NULL, "Failed to allocate memory using memory resource.\n"); + call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, 64); + + /* up to the NEW_ALIGNMENT it should use the non-aligned new/delete */ + ptr = call_func3(resource->vtbl->do_allocate, resource, 140, NEW_ALIGNMENT); + ok(ptr != NULL, "Failed to allocate memory using memory resource.\n"); + p_free(ptr); /* delete, crashes with aligned */ + + ptr = p_malloc(140); + ok(ptr != NULL, "Failed to allocate memory using _aligned_malloc.\n"); + call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, NEW_ALIGNMENT); + + /* past the NEW_ALIGNMENT it should use the aligned new/delete */ + ptr = call_func3(resource->vtbl->do_allocate, resource, 140, NEW_ALIGNMENT * 2); + 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, NEW_ALIGNMENT * 2); + ok(ptr != NULL, "Failed to allocate memory using _aligned_malloc.\n"); + call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, NEW_ALIGNMENT * 2); + + /* until the NEW_ALIGNMENT it doesn't have to be a power of two */ + ptr = call_func3(resource->vtbl->do_allocate, resource, 140, NEW_ALIGNMENT - 1); + ok(ptr != NULL, "Failed to allocate memory using memory resource.\n"); + call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, NEW_ALIGNMENT - 1); + + /* crashes with alignment not being a power of two past the NEW_ALIGNMENT */ + /* ptr = call_func3(resource->vtbl->do_allocate, resource, 140, NEW_ALIGNMENT * 2 + 1); */ + + ok((MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource), "Resource should be equal to itself.\n"); + ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, NULL), "Resource should not be equal to NULL.\n"); + ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource+1), "Resource should 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"); + + ptr = call_func3(resource->vtbl->do_allocate, resource, 140, NEW_ALIGNMENT); + ok(ptr != NULL, "Failed to allocate memory using memory resource.\n"); + call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, NEW_ALIGNMENT); + + /* up to the NEW_ALIGNMENT it is using non-aligned new/delete */ + ptr = call_func3(resource->vtbl->do_allocate, resource, 140, NEW_ALIGNMENT); + ok(ptr != NULL, "Failed to allocate memory using memory resource.\n"); + p_free(ptr); /* delete */ + + /* alignment doesn't have to be a power of two */ + ptr = call_func3(resource->vtbl->do_allocate, resource, 140, NEW_ALIGNMENT - 1); + ok(ptr != NULL, "Failed to allocate memory using memory resource.\n"); + p_free(ptr); /* delete */ + + ptr = p_malloc(140); + ok(ptr != NULL, "Failed to allocate memory using malloc.\n"); + call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, NEW_ALIGNMENT); + + /* alignment past NEW_ALIGNMENT results in bad alloc exception */ + /* ptr = call_func3(resource->vtbl->do_allocate, resource, 140, NEW_ALIGNMENT * 2); */ + + ok((MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource), "Resource should be equal to itself.\n"); + ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, NULL), "Resource should not be equal to NULL.\n"); + ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource+1), "Resource should 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), "Resource should be equal to itself.\n"); + ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, NULL), "Resource should not be equal to NULL.\n"); + ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource+1), "Resource should 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 *set_default_resource)(memory_resource *resource)) +{ + ok(p__Unaligned_get_default_resource() == p__Unaligned_new_delete_resource(), "The default aligned resource should be equal to new/delete one.\n"); + ok(p__Aligned_get_default_resource() == p__Aligned_new_delete_resource(), "The default unaligned resource should be equal to new/delete one.\n"); + + ok(set_default_resource((void*)0xdeadbeef) == new_delete_resource(), "Setting default resource should return the old one.\n"); + + /* the value is shared */ + ok(p__Unaligned_get_default_resource() == (void*)0xdeadbeef, "Setting resource should change the default unaligned resource.\n"); + ok(p__Aligned_get_default_resource() == (void*)0xdeadbeef, "Setting resource should change the default aligned resource.\n"); + + ok(set_default_resource(NULL) == (void*)0xdeadbeef, "Setting default resource should return the old one.\n"); + + ok(p__Unaligned_get_default_resource() == p__Unaligned_new_delete_resource(), "Setting the default resource to NULL should reset the unaligned default.\n"); + ok(p__Aligned_get_default_resource() == p__Aligned_new_delete_resource(), "Setting the default resource to NULL should reset the aligned default.\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_set_default_resource); + test_get_set_defult_resource(p__Unaligned_new_delete_resource, p__Unaligned_set_default_resource); + + FreeLibrary(msvcp); + FreeLibrary(ucrtbase); +}