diff --git a/src/tests/Interop/CMakeLists.txt b/src/tests/Interop/CMakeLists.txt index 289dac39c27c9a..f727473ffcf0b7 100644 --- a/src/tests/Interop/CMakeLists.txt +++ b/src/tests/Interop/CMakeLists.txt @@ -61,6 +61,7 @@ add_subdirectory(ICustomMarshaler/Primitives) add_subdirectory(LayoutClass) add_subdirectory(PInvoke/DateTime) add_subdirectory(DisabledRuntimeMarshalling) +add_subdirectory(MonoAPI/Native) if(CLR_CMAKE_TARGET_WIN32) add_subdirectory(ExecInDefAppDom) add_subdirectory(MarshalAPI/IUnknown) diff --git a/src/tests/Interop/MonoAPI/Common/MonoAPISupport.cs b/src/tests/Interop/MonoAPI/Common/MonoAPISupport.cs new file mode 100644 index 00000000000000..8c0e4594a1638b --- /dev/null +++ b/src/tests/Interop/MonoAPI/Common/MonoAPISupport.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +using System; +using System.Runtime.InteropServices; + +namespace MonoAPI.Tests; + +public class MonoAPISupport +{ + public const string TestLibName = "mono-embedding-api-test"; + + [DllImport(TestLibName)] + private static extern byte libtest_initialize_runtime_symbols(IntPtr libcoreclr_name); + + public static void Setup() + { + string libName = TestLibrary.XPlatformUtils.GetStandardNativeLibraryFileName("coreclr"); + if (!SetupSymbols(libName)) + throw new Exception ($"Native library could not probe for runtime embedding API symbols in {libName}"); + } + + private static bool SetupSymbols(string libName) + { + IntPtr ptr = IntPtr.Zero; + byte res = 0; + try { + ptr = Marshal.StringToHGlobalAnsi(libName); + res = libtest_initialize_runtime_symbols(ptr); + } finally { + Marshal.FreeHGlobal(ptr); + } + return res != 0; + } +} diff --git a/src/tests/Interop/MonoAPI/MonoMono/Directory.Build.props b/src/tests/Interop/MonoAPI/MonoMono/Directory.Build.props new file mode 100644 index 00000000000000..443760448d6c08 --- /dev/null +++ b/src/tests/Interop/MonoAPI/MonoMono/Directory.Build.props @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/mono/mono/tests/install_eh_callback.cs b/src/tests/Interop/MonoAPI/MonoMono/InstallEHCallback.cs similarity index 88% rename from src/mono/mono/tests/install_eh_callback.cs rename to src/tests/Interop/MonoAPI/MonoMono/InstallEHCallback.cs index 95fcf43ce9449f..63a29ba61e17a5 100644 --- a/src/mono/mono/tests/install_eh_callback.cs +++ b/src/tests/Interop/MonoAPI/MonoMono/InstallEHCallback.cs @@ -1,20 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// using System; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; +namespace MonoAPI.Tests.MonoMono.InstallEHCallback; + public class MonoPInvokeCallbackAttribute : Attribute { public MonoPInvokeCallbackAttribute (Type delegateType) { } } -public class Tests { +public class InstallEHCallback { - [DllImport ("libtest")] + [DllImport (MonoAPISupport.TestLibName)] public static extern void mono_test_setjmp_and_call (VoidVoidDelegate del, out IntPtr handle); - [DllImport ("libtest")] + [DllImport (MonoAPISupport.TestLibName)] public static extern void mono_test_setup_ftnptr_eh_callback (VoidVoidDelegate del, VoidHandleHandleOutDelegate inside_eh_callback); - [DllImport ("libtest")] + [DllImport (MonoAPISupport.TestLibName)] public static extern void mono_test_cleanup_ftptr_eh_callback (); public delegate void VoidVoidDelegate (); @@ -168,6 +173,15 @@ public static int test_0_throw_and_raise_exception () static int Main () { - return TestDriver.RunTests (typeof (Tests)); + MonoAPI.Tests.MonoAPISupport.Setup(); + int result; + result = test_0_setjmp_exn_handler (); + if (result != 0) + return 100 + result; + result = test_0_throw_and_raise_exception (); + if (result != 0) + return 100 + result; + return 100; } } + diff --git a/src/tests/Interop/MonoAPI/MonoMono/InstallEHCallback.csproj b/src/tests/Interop/MonoAPI/MonoMono/InstallEHCallback.csproj new file mode 100644 index 00000000000000..0a516a9a98fa0c --- /dev/null +++ b/src/tests/Interop/MonoAPI/MonoMono/InstallEHCallback.csproj @@ -0,0 +1,13 @@ + + + Exe + true + + + + + + + + + diff --git a/src/mono/mono/tests/pinvoke-detach-1.cs b/src/tests/Interop/MonoAPI/MonoMono/PInvokeDetach.cs similarity index 67% rename from src/mono/mono/tests/pinvoke-detach-1.cs rename to src/tests/Interop/MonoAPI/MonoMono/PInvokeDetach.cs index 78ef5a24250cf2..d91edbff353f52 100644 --- a/src/mono/mono/tests/pinvoke-detach-1.cs +++ b/src/tests/Interop/MonoAPI/MonoMono/PInvokeDetach.cs @@ -1,3 +1,6 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// // // pinvoke-detach-1.cs: // @@ -7,14 +10,33 @@ using System.Threading; using System.Runtime.InteropServices; +namespace MonoAPI.Tests.MonoMono.PInvokeDetach; + public class MonoPInvokeCallbackAttribute : Attribute { public MonoPInvokeCallbackAttribute (Type delegateType) { } } -public class Tests { +public class PInvokeDetach { + const string TestNamespace = "MonoAPI.Tests.MonoMono.PInvokeDetach"; + const string TestName = nameof (PInvokeDetach); + public static int Main () { - return TestDriver.RunTests (typeof (Tests)); + MonoAPI.Tests.MonoAPISupport.Setup(); + int result; + result = test_0_attach_invoke_foreign_thread (); + if (result != 0) + return 100 + result; + result = test_0_attach_invoke_foreign_thread_delegate (); + if (result != 0) + return 100 + result; + result = test_0_attach_invoke_block_foreign_thread (); + if (result != 0) + return 100 + result; + result = test_0_attach_invoke_block_foreign_thread_delegate (); + if (result != 0) + return 100 + result; + return 100; } public delegate void VoidVoidDelegate (); @@ -27,13 +49,13 @@ private static void MethodInvokedFromNative () was_called++; } - [DllImport ("libtest", EntryPoint="mono_test_attach_invoke_foreign_thread")] + [DllImport (MonoAPISupport.TestLibName, EntryPoint="mono_test_attach_invoke_foreign_thread")] public static extern bool mono_test_attach_invoke_foreign_thread (string assm_name, string name_space, string class_name, string method_name, VoidVoidDelegate del); public static int test_0_attach_invoke_foreign_thread () { was_called = 0; - bool skipped = mono_test_attach_invoke_foreign_thread (typeof (Tests).Assembly.Location, "", "Tests", "MethodInvokedFromNative", null); + bool skipped = mono_test_attach_invoke_foreign_thread (typeof (PInvokeDetach).Assembly.Location, TestNamespace, TestName, nameof(MethodInvokedFromNative), null); GC.Collect (); // should not hang waiting for the foreign thread return skipped || was_called == 5 ? 0 : 1; } @@ -60,12 +82,12 @@ private static void MethodInvokedFromNative2 () { } - [DllImport ("libtest", EntryPoint="mono_test_attach_invoke_block_foreign_thread")] + [DllImport (MonoAPISupport.TestLibName, EntryPoint="mono_test_attach_invoke_block_foreign_thread")] public static extern bool mono_test_attach_invoke_block_foreign_thread (string assm_name, string name_space, string class_name, string method_name, VoidVoidDelegate del); public static int test_0_attach_invoke_block_foreign_thread () { - bool skipped = mono_test_attach_invoke_block_foreign_thread (typeof (Tests).Assembly.Location, "", "Tests", "MethodInvokedFromNative2", null); + bool skipped = mono_test_attach_invoke_block_foreign_thread (typeof (PInvokeDetach).Assembly.Location, TestNamespace, TestName, nameof(MethodInvokedFromNative2), null); GC.Collect (); // should not hang waiting for the foreign thread return 0; // really we succeed if the app can shut down without hanging } diff --git a/src/tests/Interop/MonoAPI/MonoMono/PInvokeDetach.csproj b/src/tests/Interop/MonoAPI/MonoMono/PInvokeDetach.csproj new file mode 100644 index 00000000000000..0a516a9a98fa0c --- /dev/null +++ b/src/tests/Interop/MonoAPI/MonoMono/PInvokeDetach.csproj @@ -0,0 +1,13 @@ + + + Exe + true + + + + + + + + + diff --git a/src/mono/mono/tests/thunks.cs b/src/tests/Interop/MonoAPI/MonoMono/Thunks.cs similarity index 85% rename from src/mono/mono/tests/thunks.cs rename to src/tests/Interop/MonoAPI/MonoMono/Thunks.cs index cd59ad8bb721e1..50e7cba1c27d3c 100644 --- a/src/mono/mono/tests/thunks.cs +++ b/src/tests/Interop/MonoAPI/MonoMono/Thunks.cs @@ -1,10 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// using System; using System.Reflection; using System.Runtime.InteropServices; -public class Test +namespace MonoAPI.Tests.MonoMono.Thunks; + +public class Thunks { - [DllImport ("libtest")] + [DllImport (MonoAPISupport.TestLibName)] public static extern int test_method_thunk (int test_id, IntPtr testMethodHandle, IntPtr createObjectHandle); @@ -31,14 +36,15 @@ static void RunTests(int series, Type type) public static int Main () { - RunTests (0, typeof (Test)); + MonoAPI.Tests.MonoAPISupport.Setup(); + RunTests (0, typeof (Thunks)); RunTests (100, typeof (TestStruct)); - return 0; + return 100; } public static object CreateObject () { - return new Test (); + return new Thunks (); } public static void Test0 () @@ -102,9 +108,9 @@ public static void Test9 (ref byte a1, ref short a2, ref int a3, ref long a4, re throw new NotImplementedException (); } - public static void Test10 (ref Test obj) + public static void Test10 (ref Thunks obj) { - obj = new Test (); + obj = new Thunks (); } } diff --git a/src/tests/Interop/MonoAPI/MonoMono/Thunks.csproj b/src/tests/Interop/MonoAPI/MonoMono/Thunks.csproj new file mode 100644 index 00000000000000..0a516a9a98fa0c --- /dev/null +++ b/src/tests/Interop/MonoAPI/MonoMono/Thunks.csproj @@ -0,0 +1,13 @@ + + + Exe + true + + + + + + + + + diff --git a/src/tests/Interop/MonoAPI/Native/CMakeLists.txt b/src/tests/Interop/MonoAPI/Native/CMakeLists.txt new file mode 100644 index 00000000000000..6f9fcd85842351 --- /dev/null +++ b/src/tests/Interop/MonoAPI/Native/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_subdirectory(mono-embedding-api-test) diff --git a/src/tests/Interop/MonoAPI/Native/mono-embedding-api-test/CMakeLists.txt b/src/tests/Interop/MonoAPI/Native/mono-embedding-api-test/CMakeLists.txt new file mode 100644 index 00000000000000..10e966b6cc792e --- /dev/null +++ b/src/tests/Interop/MonoAPI/Native/mono-embedding-api-test/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.13.0) +project (MonoEmbeddingApiTest) +include_directories(${INC_PLATFORM_DIR}) + + +add_subdirectory(${CLR_SRC_NATIVE_DIR}/public public_api) + +# add the library +add_library (mono-embedding-api-test SHARED mono-embedding-api-test.c api-types.h api-functions.h) +target_link_libraries(mono-embedding-api-test monoapi ${LINK_LIBRARIES_ADDITIONAL}) + +if(CLR_CMAKE_HOST_OSX) + target_compile_definitions(mono-embedding-api-test PRIVATE -DHOST_DARWIN) +elseif(CLR_CMAKE_HOST_WIN32) + target_compile_definitions(mono-embedding-api-test PRIVATE -DHOST_WIN32 -D_CRT_SECURE_NO_WARNINGS) +else() + target_compile_definitions(mono-embedding-api-test PRIVATE -DHOST_LINUX) +endif() + +# add the install targets +install (TARGETS mono-embedding-api-test DESTINATION bin) + diff --git a/src/tests/Interop/MonoAPI/Native/mono-embedding-api-test/api-functions.h b/src/tests/Interop/MonoAPI/Native/mono-embedding-api-test/api-functions.h new file mode 100644 index 00000000000000..65cb1917eec37e --- /dev/null +++ b/src/tests/Interop/MonoAPI/Native/mono-embedding-api-test/api-functions.h @@ -0,0 +1,9 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// This file intentionally doesn't have header guards +#include +#include +#include +#include +#include diff --git a/src/tests/Interop/MonoAPI/Native/mono-embedding-api-test/api-types.h b/src/tests/Interop/MonoAPI/Native/mono-embedding-api-test/api-types.h new file mode 100644 index 00000000000000..74b083f31257d2 --- /dev/null +++ b/src/tests/Interop/MonoAPI/Native/mono-embedding-api-test/api-types.h @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#ifndef TEST_EMBEDDING_API_TYPES_H +#define TEST_EMBEDDING_API_TYPES_H + +#include +#include +#include +#include +#include +#include + +MONO_BEGIN_DECLS + + + +MONO_END_DECLS + +#endif /*TEST_EMBEDDING_API_TYPES_H*/ diff --git a/src/tests/Interop/MonoAPI/Native/mono-embedding-api-test/mono-embedding-api-test.c b/src/tests/Interop/MonoAPI/Native/mono-embedding-api-test/mono-embedding-api-test.c new file mode 100644 index 00000000000000..35b767fd4cce9c --- /dev/null +++ b/src/tests/Interop/MonoAPI/Native/mono-embedding-api-test/mono-embedding-api-test.c @@ -0,0 +1,1091 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif + +#ifndef HOST_WIN32 +#include +#endif + +#ifdef WIN32 +#include +#else +#include +#endif + +#ifndef WIN32 +#define S_OK 0x0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef WIN32 +#define STDCALL __stdcall +#else +#define STDCALL +#define __thiscall /* nothing */ +#endif + +#ifdef WIN32 +extern __declspec(dllimport) void __stdcall CoTaskMemFree(void *ptr); +#endif + +typedef int (STDCALL *SimpleDelegate) (int a); + +#if defined(WIN32) && defined (_MSC_VER) +#define LIBTEST_API __declspec(dllexport) +#elif defined(__GNUC__) +#define LIBTEST_API __attribute__ ((__visibility__ ("default"))) +#else +#define LIBTEST_API +#endif + +#define FALSE 0 +#define TRUE 1 + +typedef void * gpointer; + +static void +g_usleep (unsigned long micros) +{ +#ifdef WIN32 + Sleep (micros/1000); +#else + usleep (micros); +#endif +} + +static gpointer +g_malloc (size_t x) +{ + return malloc (x); +} + +static gpointer +g_malloc0 (size_t x) +{ + return calloc (1, x); +} + +static void +g_free (void *ptr) +{ + free (ptr); +} + +static void marshal_free (void *ptr) +{ +#ifdef WIN32 + CoTaskMemFree (ptr); +#else + g_free (ptr); +#endif +} + +static void* marshal_alloc (size_t size) +{ +#ifdef WIN32 + return CoTaskMemAlloc (size); +#else + return g_malloc (size); +#endif +} + +static char* marshal_strdup (const char *str) +{ +#ifdef WIN32 + if (!str) + return NULL; + + size_t n = strlen (str) + 1; + char *buf = (char *) CoTaskMemAlloc (n); + strncpy_s (buf, n, str, n - 1); + buf[n] = 0; + return buf; +#else + return strdup (str); +#endif +} + +static char *libtest_api_coreclr_name; + +static uint8_t +mono_test_init_symbols (void); + +LIBTEST_API uint8_t +libtest_initialize_runtime_symbols (const char *libcoreclr_name) +{ + libtest_api_coreclr_name = marshal_strdup (libcoreclr_name); + return mono_test_init_symbols(); +} +#ifdef WIN32 +// Copied from eglib gmodule-win32.c +#if HAVE_API_SUPPORT_WIN32_ENUM_PROCESS_MODULES +static gpointer +w32_find_symbol (const char *symbol_name) +{ + HMODULE *modules; + DWORD buffer_size = sizeof (HMODULE) * 1024; + DWORD needed, i; + + modules = (HMODULE *) g_malloc (buffer_size); + + if (modules == NULL) + return NULL; + + if (!EnumProcessModules (GetCurrentProcess (), modules, + buffer_size, &needed)) { + g_free (modules); + return NULL; + } + + /* check whether the supplied buffer was too small, realloc, retry */ + if (needed > buffer_size) { + g_free (modules); + + buffer_size = needed; + modules = (HMODULE *) g_malloc (buffer_size); + + if (modules == NULL) + return NULL; + + if (!EnumProcessModules (GetCurrentProcess (), modules, + buffer_size, &needed)) { + g_free (modules); + return NULL; + } + } + + for (i = 0; i < needed / sizeof (HANDLE); i++) { + gpointer proc = (gpointer)(intptr_t)GetProcAddress (modules [i], symbol_name); + if (proc != NULL) { + g_free (modules); + return proc; + } + } + + g_free (modules); + return NULL; +} +#elif !HAVE_EXTERN_DEFINED_WIN32_ENUM_PROCESS_MODULES +static gpointer +w32_find_symbol (const char *symbol_name) +{ + SetLastError (ERROR_NOT_SUPPORTED); + return NULL; +} +#endif +#endif/*WIN32*/ + +#ifdef WIN32 +static HMODULE libcoreclr_module; +#else +static void* libcoreclr_module; +#endif + +/* Searches for mono symbols in all loaded modules */ +static gpointer +lookup_mono_symbol (const char *symbol_name) +{ +#ifndef HOST_WIN32 + if (!libcoreclr_module) + libcoreclr_module = dlopen (libtest_api_coreclr_name, RTLD_LAZY); + return dlsym (/*RTLD_DEFAULT*/ libcoreclr_module, symbol_name); +#else + if (!libcoreclr_module) + libcoreclr_module = GetModuleHandle (NULL); + gpointer symbol = NULL; + symbol = (gpointer)(intptr_t)GetProcAddress(libcoreclr_module, symbol_name); + if (symbol) + return symbol; + return w32_find_symbol (symbol_name); +#endif +} + +typedef void (*MonoFtnPtrEHCallback) (uint32_t gchandle); + +static int sym_inited = 0; + +/* MONO_API functions that aren't in public headers */ +static void (*sym_mono_install_ftnptr_eh_callback) (MonoFtnPtrEHCallback); +static void (*sym_mono_threads_exit_gc_safe_region_unbalanced) (gpointer, gpointer *); + +static void (*null_function_ptr) (void); + +static void (*sym_mono_threads_exit_gc_unsafe_region) (void*, void*); + +static void* (*sym_mono_threads_enter_gc_unsafe_region) (void** stackdata); + +#include "api-types.h" + +#define MONO_API_FUNCTION(ret,name,args) static ret (*sym_ ## name) args; +#include "api-functions.h" +#undef MONO_API_FUNCTION + +// FIXME use runtime headers +#define MONO_BEGIN_EFRAME { void *__dummy; void *__region_cookie = mono_threads_enter_gc_unsafe_region ? mono_threads_enter_gc_unsafe_region (&__dummy) : NULL; +#define MONO_END_EFRAME if (mono_threads_exit_gc_unsafe_region) mono_threads_exit_gc_unsafe_region (__region_cookie, &__dummy); } + + +/* + * mono_method_get_unmanaged_thunk tests + */ + +/* thunks.cs:TestStruct */ +typedef struct _TestStruct { + int A; + double B; +} TestStruct; + +/** + * test_method_thunk: + * + * @test_id: the test number + * @test_method_handle: MonoMethod* of the C# test method + * @create_object_method_handle: MonoMethod* of thunks.cs:Test.CreateObject + */ +LIBTEST_API int STDCALL +test_method_thunk (int test_id, gpointer test_method_handle, gpointer create_object_method_handle) +{ + int ret = 0; + + gpointer (*mono_method_get_unmanaged_thunk)(gpointer) + = (gpointer (*)(gpointer))sym_mono_method_get_unmanaged_thunk; + + gpointer (*mono_string_new_wrapper)(const char *) + = (gpointer (*)(const char *))sym_mono_string_new_wrapper; + + char *(*mono_string_to_utf8)(gpointer) + = (char *(*)(gpointer))sym_mono_string_to_utf8; + + // FIXME use runtime headers + gpointer (*mono_object_unbox)(gpointer) + = (gpointer (*)(gpointer))sym_mono_object_unbox; + + // FIXME use runtime headers + gpointer (*mono_threads_enter_gc_unsafe_region) (gpointer) + = (gpointer (*)(gpointer))sym_mono_threads_enter_gc_unsafe_region; + + // FIXME use runtime headers + void (*mono_threads_exit_gc_unsafe_region) (gpointer, gpointer) + = (void (*)(gpointer, gpointer))sym_mono_threads_exit_gc_unsafe_region; + + + + gpointer test_method, ex = NULL; + gpointer (STDCALL *CreateObject)(gpointer*); + + MONO_BEGIN_EFRAME; + + if (!mono_method_get_unmanaged_thunk) { + ret = 1; + goto done; + } + + test_method = mono_method_get_unmanaged_thunk (test_method_handle); + if (!test_method) { + ret = 2; + goto done; + } + + CreateObject = (gpointer (STDCALL *)(gpointer *))mono_method_get_unmanaged_thunk (create_object_method_handle); + if (!CreateObject) { + ret = 3; + goto done; + } + + + switch (test_id) { + + case 0: { + /* thunks.cs:Test.Test0 */ + void (STDCALL *F)(gpointer *) = (void (STDCALL *)(gpointer *))test_method; + F (&ex); + break; + } + + case 1: { + /* thunks.cs:Test.Test1 */ + int (STDCALL *F)(gpointer *) = (int (STDCALL *)(gpointer *))test_method; + if (F (&ex) != 42) { + ret = 4; + goto done; + } + break; + } + + case 2: { + /* thunks.cs:Test.Test2 */ + gpointer (STDCALL *F)(gpointer, gpointer*) = (gpointer (STDCALL *)(gpointer, gpointer *))test_method; + gpointer str = mono_string_new_wrapper ("foo"); + if (str != F (str, &ex)) { + ret = 4; + goto done; + } + break; + } + + case 3: { + /* thunks.cs:Test.Test3 */ + gpointer (STDCALL *F)(gpointer, gpointer, gpointer*); + gpointer obj; + gpointer str; + + F = (gpointer (STDCALL *)(gpointer, gpointer, gpointer *))test_method; + obj = CreateObject (&ex); + str = mono_string_new_wrapper ("bar"); + + if (str != F (obj, str, &ex)) { + ret = 4; + goto done; + } + break; + } + + case 4: { + /* thunks.cs:Test.Test4 */ + int (STDCALL *F)(gpointer, gpointer, int, gpointer*); + gpointer obj; + gpointer str; + + F = (int (STDCALL *)(gpointer, gpointer, int, gpointer *))test_method; + obj = CreateObject (&ex); + str = mono_string_new_wrapper ("bar"); + + if (42 != F (obj, str, 42, &ex)) { + ret = 4; + goto done; + } + + break; + } + + case 5: { + /* thunks.cs:Test.Test5 */ + int (STDCALL *F)(gpointer, gpointer, int, gpointer*); + gpointer obj; + gpointer str; + + F = (int (STDCALL *)(gpointer, gpointer, int, gpointer *))test_method; + obj = CreateObject (&ex); + str = mono_string_new_wrapper ("bar"); + + F (obj, str, 42, &ex); + if (!ex) { + ret = 4; + goto done; + } + + break; + } + + case 6: { + /* thunks.cs:Test.Test6 */ + int (STDCALL *F)(gpointer, uint8_t, int16_t, int32_t, int64_t, float, double, + gpointer, gpointer*); + gpointer obj; + gpointer str = mono_string_new_wrapper ("Test6"); + int res; + + F = (int (STDCALL *)(gpointer, uint8_t, int16_t, int32_t, int64_t, float, double, gpointer, gpointer *))test_method; + obj = CreateObject (&ex); + + res = F (obj, 254, 32700, -245378, 6789600, 3.1415f, 3.1415, str, &ex); + if (ex) { + ret = 4; + goto done; + } + + if (!res) { + ret = 5; + goto done; + } + + break; + } + + case 7: { + /* thunks.cs:Test.Test7 */ + int64_t (STDCALL *F)(gpointer*) = (int64_t (STDCALL *)(gpointer *))test_method; + if (F (&ex) != INT64_MAX) { + ret = 4; + goto done; + } + break; + } + + case 8: { + /* thunks.cs:Test.Test8 */ + void (STDCALL *F)(uint8_t*, int16_t*, int32_t*, int64_t*, float*, double*, + gpointer*, gpointer*); + + uint8_t a1; + int16_t a2; + int32_t a3; + int64_t a4; + float a5; + double a6; + gpointer a7; + + F = (void (STDCALL *)(uint8_t *, int16_t *, int32_t *, int64_t *, float *, double *, + gpointer *, gpointer *))test_method; + + F (&a1, &a2, &a3, &a4, &a5, &a6, &a7, &ex); + if (ex) { + ret = 4; + goto done; + } + + if (!(a1 == 254 && + a2 == 32700 && + a3 == -245378 && + a4 == 6789600 && + (fabs (a5 - 3.1415) < 0.001) && + (fabs (a6 - 3.1415) < 0.001) && + strcmp (mono_string_to_utf8 (a7), "Test8") == 0)){ + ret = 5; + goto done; + } + + break; + } + + case 9: { + /* thunks.cs:Test.Test9 */ + void (STDCALL *F)(uint8_t*, int16_t*, int32_t*, int64_t*, float*, double*, + gpointer*, gpointer*); + + uint8_t a1; + int16_t a2; + int32_t a3; + int64_t a4; + float a5; + double a6; + gpointer a7; + + F = (void (STDCALL *)(uint8_t *, int16_t *, int32_t *, int64_t *, float *, double *, + gpointer *, gpointer *))test_method; + + F (&a1, &a2, &a3, &a4, &a5, &a6, &a7, &ex); + if (!ex) { + ret = 4; + goto done; + } + + break; + } + + case 10: { + /* thunks.cs:Test.Test10 */ + void (STDCALL *F)(gpointer*, gpointer*); + + gpointer obj1, obj2; + + obj1 = obj2 = CreateObject (&ex); + if (ex) { + ret = 4; + goto done; + } + + F = (void (STDCALL *)(gpointer *, gpointer *))test_method; + + F (&obj1, &ex); + if (ex) { + ret = 5; + goto done; + } + + if (obj1 == obj2) { + ret = 6; + goto done; + } + + break; + } + + case 100: { + /* thunks.cs:TestStruct.Test0 */ + int (STDCALL *F)(gpointer*, gpointer*); + + gpointer obj; + TestStruct *a1; + int res; + + obj = CreateObject (&ex); + if (ex) { + ret = 4; + goto done; + } + + if (!obj) { + ret = 5; + goto done; + } + + a1 = (TestStruct *)mono_object_unbox (obj); + if (!a1) { + ret = 6; + goto done; + } + + a1->A = 42; + a1->B = 3.1415; + + F = (int (STDCALL *)(gpointer *, gpointer *))test_method; + + res = F ((gpointer *)obj, &ex); + if (ex) { + ret = 7; + goto done; + } + + if (!res) { + ret = 8; + goto done; + } + + /* check whether the call was really by value */ + if (a1->A != 42 || a1->B != 3.1415) { + ret = 9; + goto done; + } + + break; + } + + case 101: { + /* thunks.cs:TestStruct.Test1 */ + void (STDCALL *F)(gpointer, gpointer*); + + TestStruct *a1; + gpointer obj; + + obj = CreateObject (&ex); + if (ex) { + ret = 4; + goto done; + } + + if (!obj) { + ret = 5; + goto done; + } + + a1 = (TestStruct *)mono_object_unbox (obj); + if (!a1) { + ret = 6; + goto done; + } + + F = (void (STDCALL *)(gpointer, gpointer *))test_method; + + F (obj, &ex); + if (ex) { + ret = 7; + goto done; + } + + if (a1->A != 42) { + ret = 8; + goto done; + } + + if (!(fabs (a1->B - 3.1415) < 0.001)) { + ret = 9; + goto done; + } + + break; + } + + case 102: { + /* thunks.cs:TestStruct.Test2 */ + gpointer (STDCALL *F)(gpointer*); + + TestStruct *a1; + gpointer obj; + + F = (gpointer (STDCALL *)(gpointer *))test_method; + + obj = F (&ex); + if (ex) { + ret = 4; + goto done; + } + + if (!obj) { + ret = 5; + goto done; + } + + a1 = (TestStruct *)mono_object_unbox (obj); + + if (a1->A != 42) { + ret = 5; + goto done; + } + + if (!(fabs (a1->B - 3.1415) < 0.001)) { + ret = 6; + goto done; + } + + break; + } + + case 103: { + /* thunks.cs:TestStruct.Test3 */ + void (STDCALL *F)(gpointer, gpointer*); + + TestStruct *a1; + gpointer obj; + + obj = CreateObject (&ex); + if (ex) { + ret = 4; + goto done; + } + + if (!obj) { + ret = 5; + goto done; + } + + a1 = (TestStruct *)mono_object_unbox (obj); + + if (!a1) { + ret = 6; + goto done; + } + + a1->A = 42; + a1->B = 3.1415; + + F = (void (STDCALL *)(gpointer, gpointer *))test_method; + + F (obj, &ex); + if (ex) { + ret = 4; + goto done; + } + + if (a1->A != 1) { + ret = 5; + goto done; + } + + if (a1->B != 17) { + ret = 6; + goto done; + } + + break; + } + + default: + ret = 9; + + } +done: + MONO_END_EFRAME; + + return ret; +} + +static int call_managed_res; + +static void +call_managed (gpointer arg) +{ + SimpleDelegate del = (SimpleDelegate)arg; + + call_managed_res = del (42); +} + +LIBTEST_API int STDCALL +mono_test_marshal_thread_attach (SimpleDelegate del) +{ +#ifdef WIN32 + return 43; +#else + int res; + pthread_t t; + + res = pthread_create (&t, NULL, (gpointer (*)(gpointer))call_managed, (gpointer)del); + assert (res == 0); + pthread_join (t, NULL); + + return call_managed_res; +#endif +} + +typedef struct { + char arr [4 * 1024]; +} LargeStruct; + +typedef int (STDCALL *LargeStructDelegate) (LargeStruct *s); + +static void +call_managed_large_vt (gpointer arg) +{ + LargeStructDelegate del = (LargeStructDelegate)arg; + LargeStruct s; + + call_managed_res = del (&s); +} + +LIBTEST_API int STDCALL +mono_test_marshal_thread_attach_large_vt (SimpleDelegate del) +{ +#ifdef WIN32 + return 43; +#else + int res; + pthread_t t; + + res = pthread_create (&t, NULL, (gpointer (*)(gpointer))call_managed_large_vt, (gpointer)del); + assert (res == 0); + pthread_join (t, NULL); + + return call_managed_res; +#endif +} + +#ifndef WIN32 + +typedef void (*NativeToManagedExceptionRethrowFunc) (void); + +void *mono_test_native_to_managed_exception_rethrow_thread (void *arg) +{ + NativeToManagedExceptionRethrowFunc func = (NativeToManagedExceptionRethrowFunc) arg; + func (); + return NULL; +} + +LIBTEST_API void STDCALL +mono_test_native_to_managed_exception_rethrow (NativeToManagedExceptionRethrowFunc func) +{ + pthread_t t; + pthread_create (&t, NULL, mono_test_native_to_managed_exception_rethrow_thread, (gpointer)func); + pthread_join (t, NULL); +} +#endif + +// SYM_LOOKUP(mono_runtime_invoke) +// expands to +// sym_mono_runtime_invoke = lookup_mono_symbol ("mono_runtime_invoke"); +#define SYM_LOOKUP(name) do { \ + sym_##name = lookup_mono_symbol (#name); \ + } while (0) + +static uint8_t +mono_test_init_symbols (void) +{ + if (sym_inited) + return 1; + + SYM_LOOKUP (mono_install_ftnptr_eh_callback); + assert (sym_mono_install_ftnptr_eh_callback); + SYM_LOOKUP (mono_gchandle_get_target); + SYM_LOOKUP (mono_gchandle_new); + SYM_LOOKUP (mono_gchandle_free); + SYM_LOOKUP (mono_raise_exception); + SYM_LOOKUP (mono_domain_unload); + SYM_LOOKUP (mono_threads_exit_gc_safe_region_unbalanced); + + SYM_LOOKUP (mono_get_root_domain); + SYM_LOOKUP (mono_domain_get); + SYM_LOOKUP (mono_domain_set); + SYM_LOOKUP (mono_domain_assembly_open); + SYM_LOOKUP (mono_assembly_get_image); + SYM_LOOKUP (mono_class_from_name); + SYM_LOOKUP (mono_class_get_method_from_name); + SYM_LOOKUP (mono_thread_attach); + SYM_LOOKUP (mono_thread_detach); + SYM_LOOKUP (mono_runtime_invoke); + + SYM_LOOKUP (mono_method_get_unmanaged_thunk); + SYM_LOOKUP (mono_string_new_wrapper); + SYM_LOOKUP (mono_string_to_utf8); + SYM_LOOKUP (mono_object_unbox); + + SYM_LOOKUP (mono_threads_enter_gc_unsafe_region); + SYM_LOOKUP (mono_threads_exit_gc_unsafe_region); + + sym_inited = 1; + + return ((void*)sym_mono_gchandle_new != NULL); + +} + +#ifndef TARGET_WASM + +static jmp_buf test_jmp_buf; +static uint32_t test_gchandle; + +static void +mono_test_longjmp_callback (uint32_t gchandle) +{ + test_gchandle = gchandle; + longjmp (test_jmp_buf, 1); +} + +typedef void (*VoidVoidCallback) (void); + +LIBTEST_API void STDCALL +mono_test_setjmp_and_call (VoidVoidCallback managedCallback, intptr_t *out_handle) +{ + if (setjmp (test_jmp_buf) == 0) { + *out_handle = 0; + sym_mono_install_ftnptr_eh_callback (mono_test_longjmp_callback); + managedCallback (); + *out_handle = 0; /* Do not expect to return here */ + } else { + sym_mono_install_ftnptr_eh_callback (NULL); + *out_handle = test_gchandle; + } +} + +#endif + +LIBTEST_API void STDCALL +mono_test_marshal_bstr (void *ptr) +{ +} + +static void (*mono_test_capture_throw_callback) (uint32_t gchandle, uint32_t *exception_out); + +static void +mono_test_ftnptr_eh_callback (uint32_t gchandle) +{ + uint32_t exception_handle = 0; + + assert (gchandle != 0); + MonoObject *exc = sym_mono_gchandle_get_target (gchandle); + sym_mono_gchandle_free (gchandle); + + uint32_t handle = sym_mono_gchandle_new (exc, FALSE); + mono_test_capture_throw_callback (handle, &exception_handle); + sym_mono_gchandle_free (handle); + + assert (exception_handle != 0); + exc = sym_mono_gchandle_get_target (exception_handle); + sym_mono_gchandle_free (exception_handle); + + sym_mono_raise_exception ((MonoException*)exc); + assert (((void)"mono_raise_exception should not return", 0)); +} + +LIBTEST_API void STDCALL +mono_test_setup_ftnptr_eh_callback (VoidVoidCallback managed_entry, void (*capture_throw_callback) (uint32_t, uint32_t *)) +{ + mono_test_capture_throw_callback = capture_throw_callback; + sym_mono_install_ftnptr_eh_callback (mono_test_ftnptr_eh_callback); + managed_entry (); +} + +LIBTEST_API void STDCALL +mono_test_cleanup_ftptr_eh_callback (void) +{ + sym_mono_install_ftnptr_eh_callback (NULL); +} + +struct invoke_names { + char *assm_name; + char *name_space; + char *name; + char *meth_name; +}; + +static struct invoke_names * +make_invoke_names (const char *assm_name, const char *name_space, const char *name, const char *meth_name) +{ + struct invoke_names *names = (struct invoke_names*) marshal_alloc (sizeof (struct invoke_names)); + names->assm_name = marshal_strdup (assm_name); + names->name_space = marshal_strdup (name_space); + names->name = marshal_strdup (name); + names->meth_name = marshal_strdup (meth_name); + return names; +} + +static void +destroy_invoke_names (struct invoke_names *n) +{ + marshal_free (n->assm_name); + marshal_free (n->name_space); + marshal_free (n->name); + marshal_free (n->meth_name); + marshal_free (n); +} + +static void +test_invoke_by_name (struct invoke_names *names) +{ + MonoDomain *domain = sym_mono_domain_get (); + MonoThread *thread = NULL; + if (!domain) { + thread = sym_mono_thread_attach (sym_mono_get_root_domain ()); + } + domain = sym_mono_domain_get (); + assert (domain); + MonoAssembly *assm = sym_mono_domain_assembly_open (domain, names->assm_name); + assert (assm); + MonoImage *image = sym_mono_assembly_get_image (assm); + MonoClass *klass = sym_mono_class_from_name (image, names->name_space, names->name); + assert (klass); + /* meth_name should be a static method that takes no arguments */ + MonoMethod *method = sym_mono_class_get_method_from_name (klass, names->meth_name, -1); + assert (method); + + MonoObject *args[] = {NULL, }; + + sym_mono_runtime_invoke (method, NULL, (void**)args, NULL); + + if (thread) + sym_mono_thread_detach (thread); +} + +#ifndef HOST_WIN32 +static void* +invoke_foreign_thread (void* user_data) +{ + struct invoke_names *names = (struct invoke_names*)user_data; + /* + * Run a couple of times to check that attach/detach multiple + * times from the same thread leaves it in a reasonable coop + * thread state. + */ + for (int i = 0; i < 5; ++i) { + test_invoke_by_name (names); + g_usleep (1000); + } + destroy_invoke_names (names); + return NULL; +} + +static void* +invoke_foreign_delegate (void *user_data) +{ + VoidVoidCallback del = (VoidVoidCallback)user_data; + for (int i = 0; i < 5; ++i) { + del (); + g_usleep (1000); + } + return NULL; +} + +#endif + + +LIBTEST_API mono_bool STDCALL +mono_test_attach_invoke_foreign_thread (const char *assm_name, const char *name_space, const char *name, const char *meth_name, VoidVoidCallback del) +{ +#ifndef HOST_WIN32 + if (!del) { + struct invoke_names *names = make_invoke_names (assm_name, name_space, name, meth_name); + pthread_t t; + int res = pthread_create (&t, NULL, invoke_foreign_thread, (void*)names); + assert (res == 0); + pthread_join (t, NULL); + return 0; + } else { + pthread_t t; + int res = pthread_create (&t, NULL, invoke_foreign_delegate, (void*)del); + assert (res == 0); + pthread_join (t, NULL); + return 0; + } +#else + // TODO: Win32 version of this test + return 1; +#endif +} + +#ifndef HOST_WIN32 +struct names_and_mutex { + /* if del is NULL, use names, otherwise just call del */ + VoidVoidCallback del; + struct invoke_names *names; + /* mutex to coordinate test and foreign thread */ + pthread_mutex_t coord_mutex; + pthread_cond_t coord_cond; + /* mutex to block the foreign thread */ + pthread_mutex_t deadlock_mutex; +}; + +static void* +invoke_block_foreign_thread (void *user_data) +{ + // This thread calls into the runtime and then blocks. It should not + // prevent the runtime from shutting down. + struct names_and_mutex *nm = (struct names_and_mutex *)user_data; + if (!nm->del) { + test_invoke_by_name (nm->names); + } else { + nm->del (); + } + pthread_mutex_lock (&nm->coord_mutex); + /* signal the test thread that we called the runtime */ + pthread_cond_signal (&nm->coord_cond); + pthread_mutex_unlock (&nm->coord_mutex); + + pthread_mutex_lock (&nm->deadlock_mutex); // blocks forever + fprintf(stderr, "did not expect to reach past deadlock\n"); + abort(); +} +#endif + +LIBTEST_API mono_bool STDCALL +mono_test_attach_invoke_block_foreign_thread (const char *assm_name, const char *name_space, const char *name, const char *meth_name, VoidVoidCallback del) +{ +#ifndef HOST_WIN32 + struct names_and_mutex *nm = (struct names_and_mutex *)malloc (sizeof (struct names_and_mutex)); + nm->del = del; + if (!del) { + struct invoke_names *names = make_invoke_names (assm_name, name_space, name, meth_name); + nm->names = names; + } else { + nm->names = NULL; + } + pthread_mutex_init (&nm->coord_mutex, NULL); + pthread_cond_init (&nm->coord_cond, NULL); + pthread_mutex_init (&nm->deadlock_mutex, NULL); + + pthread_mutex_lock (&nm->deadlock_mutex); // lock the mutex and never unlock it. + pthread_t t; + int res = pthread_create (&t, NULL, invoke_block_foreign_thread, (void*)nm); + assert (res == 0); + /* wait for the foreign thread to finish calling the runtime before + * detaching it and returning + */ + pthread_mutex_lock (&nm->coord_mutex); + pthread_cond_wait (&nm->coord_cond, &nm->coord_mutex); + pthread_mutex_unlock (&nm->coord_mutex); + pthread_detach (t); + return 0; +#else + // TODO: Win32 version of this test + return 1; +#endif +} + +#ifdef __cplusplus +} // extern C +#endif diff --git a/src/tests/issues.targets b/src/tests/issues.targets index c5e27cc88c43aa..70306cca68b78c 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -60,6 +60,9 @@ https://github.com/dotnet/runtime/issues/57786 + + CoreCLR does not implement the mono embedding API + @@ -2918,6 +2921,9 @@ needs triage + + Test not expected to work with AOT + @@ -3847,4 +3853,10 @@ missing assembly + + + + mobile and wasm don't support tests with native libraries. wasm also needs static linking + +