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
+
+