+2008-05-03 Robert Jordan <robertj@gmx.net>
+
+ * image.c, metadata-internals.h: add thunk_invoke_cache.
+
+ * marshal.c, marshal.h: implement
+ mono_marshal_get_thunk_invoke_wrapper ().
+
+ * object.c, object.h: implement mono_method_get_unmanaged_thunk ().
+
+ Code is contributed under MIT/X11 license.
+
2008-05-02 Rodrigo Kumpera <rkumpera@novell.com>
* verify.c (do_leave): Empty the stack.
image->isinst_cache = g_hash_table_new (mono_aligned_addr_hash, NULL);
image->castclass_cache = g_hash_table_new (mono_aligned_addr_hash, NULL);
image->proxy_isinst_cache = g_hash_table_new (mono_aligned_addr_hash, NULL);
+ image->thunk_invoke_cache = g_hash_table_new (mono_aligned_addr_hash, NULL);
image->typespec_cache = g_hash_table_new (NULL, NULL);
image->memberref_signatures = g_hash_table_new (NULL, NULL);
g_hash_table_destroy (image->isinst_cache);
g_hash_table_destroy (image->castclass_cache);
g_hash_table_destroy (image->proxy_isinst_cache);
+ g_hash_table_destroy (image->thunk_invoke_cache);
if (image->static_rgctx_invoke_cache)
g_hash_table_destroy (image->static_rgctx_invoke_cache);
*byte_offset = i;
*bitmask = buf [i];
}
+
+MonoMethod *
+mono_marshal_get_thunk_invoke_wrapper (MonoMethod *method)
+{
+ MonoMethodBuilder *mb;
+ MonoMethodSignature *sig, *csig;
+ MonoExceptionClause *clause;
+ MonoMethodHeader *header;
+ MonoImage *image;
+ MonoClass *klass;
+ GHashTable *cache;
+ MonoMethod *res;
+ int i, param_count, sig_size, pos_leave;
+
+ g_assert (method);
+
+ klass = method->klass;
+ image = method->klass->image;
+ cache = image->thunk_invoke_cache;
+
+ if ((res = mono_marshal_find_in_cache (cache, method)))
+ return res;
+
+ sig = mono_method_signature (method);
+ mb = mono_mb_new (klass, method->name, MONO_WRAPPER_NATIVE_TO_MANAGED);
+
+ /* add "this" and exception param */
+ param_count = sig->param_count + sig->hasthis + 1;
+
+ /* dup & extend signature */
+ csig = mono_metadata_signature_alloc (image, param_count);
+ sig_size = sizeof (MonoMethodSignature) + ((sig->param_count - MONO_ZERO_LEN_ARRAY) * sizeof (MonoType *));
+ memcpy (csig, sig, sig_size);
+ csig->param_count = param_count;
+ csig->hasthis = 0;
+ csig->pinvoke = 1;
+ csig->call_convention = MONO_CALL_DEFAULT;
+
+ if (sig->hasthis) {
+ /* "this" of value types is actually a ptr */
+ csig->params [0] = klass->valuetype
+ ? &mono_ptr_class_get (&klass->byval_arg)->byval_arg
+ : &klass->byval_arg;
+ /* shift params */
+ for (i = 0; i < sig->param_count; i++)
+ csig->params [i + 1] = sig->params [i];
+ }
+
+ /* setup exception param as byref+[out] */
+ csig->params [param_count - 1] = mono_metadata_type_dup (image->mempool,
+ &mono_defaults.exception_class->byval_arg);
+ csig->params [param_count - 1]->byref = 1;
+ csig->params [param_count - 1]->attrs = PARAM_ATTRIBUTE_OUT;
+
+ /* local 0 (temp for exception object) */
+ mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg);
+
+ /* local 1 (temp for result) */
+ if (!MONO_TYPE_IS_VOID (sig->ret))
+ mono_mb_add_local (mb, sig->ret);
+
+ /* clear exception arg */
+ mono_mb_emit_ldarg (mb, param_count - 1);
+ mono_mb_emit_byte (mb, CEE_LDNULL);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+
+ /* try */
+ mono_loader_lock ();
+ clause = mono_mempool_alloc0 (image->mempool, sizeof (MonoExceptionClause));
+ mono_loader_unlock ();
+ clause->try_offset = mono_mb_get_label (mb);
+
+ /* push method's args */
+ for (i = 0; i < param_count - 1; i++)
+ mono_mb_emit_ldarg (mb, i);
+
+ /* call */
+ if (method->flags & METHOD_ATTRIBUTE_VIRTUAL)
+ mono_mb_emit_op (mb, CEE_CALLVIRT, method);
+ else
+ mono_mb_emit_op (mb, CEE_CALL, method);
+
+ /* save result at local 1 */
+ if (!MONO_TYPE_IS_VOID (sig->ret))
+ mono_mb_emit_stloc (mb, 1);
+
+ pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
+
+ /* catch */
+ clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
+ clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
+ clause->data.catch_class = mono_defaults.object_class;
+
+ clause->handler_offset = mono_mb_get_label (mb);
+
+ /* store exception at local 0 */
+ mono_mb_emit_stloc (mb, 0);
+ mono_mb_emit_ldarg (mb, param_count - 1);
+ mono_mb_emit_ldloc (mb, 0);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+ mono_mb_emit_branch (mb, CEE_LEAVE);
+
+ clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
+
+ mono_mb_patch_branch (mb, pos_leave);
+ /* end-try */
+
+ if (!MONO_TYPE_IS_VOID (sig->ret))
+ mono_mb_emit_ldloc (mb, 1);
+
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ res = mono_mb_create_and_cache (cache, method, mb, csig, param_count + 16);
+ mono_mb_free (mb);
+
+ header = ((MonoMethodNormal *)res)->header;
+ header->num_clauses = 1;
+ header->clauses = clause;
+
+ return res;
+}
mono_marshal_get_generic_array_helper (MonoClass *class, MonoClass *iface,
gchar *name, MonoMethod *method) MONO_INTERNAL;
+MonoMethod *
+mono_marshal_get_thunk_invoke_wrapper (MonoMethod *method) MONO_INTERNAL;
+
/* marshaling internal calls */
void *
GHashTable *cominterop_invoke_cache;
GHashTable *cominterop_wrapper_cache;
GHashTable *static_rgctx_invoke_cache; /* LOCKING: marshal lock */
+ GHashTable *thunk_invoke_cache;
/*
* indexed by MonoClass pointers
return default_mono_runtime_invoke (method, obj, params, exc);
}
+/**
+ * mono_method_get_unmanaged_thunk:
+ * @method: method to generate a thunk for.
+ *
+ * Returns an unmanaged->managed thunk that can be used to call
+ * a managed method directly from C.
+ *
+ * The thunk's C signature closely matches the managed signature:
+ *
+ * C#: public bool Equals (object obj);
+ * C: typedef MonoBoolean (*Equals)(MonoObject *this,
+ * MonoObject *obj, MonoException **ex);
+ *
+ * The "this" parameter must not be used with static methods:
+ *
+ * C#: public static bool ReferenceEquals (object a, object b);
+ * C: typedef MonoBoolean (*ReferenceEquals)(MonoObject *a, MonoObject *b,
+ * MonoException **ex);
+ *
+ * The last argument must be a non-null pointer of a MonoException* pointer.
+ * It has "out" semantics. After invoking the thunk, *ex will be NULL if no
+ * exception has been thrown in managed code. Otherwise, it will point
+ * to the MonoException* caught by the thunk. In this case, the result of
+ * the thunk is undefined:
+ *
+ * MonoMethod *method = ... // MonoMethod* of System.Object.Equals
+ * MonoException *ex = NULL;
+ * Equals func = mono_method_get_unmanaged_thunk (method);
+ * MonoBoolean res = func (thisObj, objToCompare, &ex);
+ * if (ex) {
+ * // handle exception
+ * }
+ *
+ * The calling convention of the thunk matches the platform's default
+ * convention. This means that under Windows, C declarations must
+ * contain the __stdcall attribute:
+ *
+ * C: typedef MonoBoolean (__stdcall *Equals)(MonoObject *this,
+ * MonoObject *obj, MonoException **ex);
+ */
+gpointer
+mono_method_get_unmanaged_thunk (MonoMethod *method)
+{
+ method = mono_marshal_get_thunk_invoke_wrapper (method);
+ return mono_compile_method (method);
+}
+
static void
set_value (MonoType *type, void *dest, void *value, int deref_pointer)
{
mono_runtime_invoke_array (MonoMethod *method, void *obj, MonoArray *params,
MonoObject **exc);
+gpointer
+mono_method_get_unmanaged_thunk (MonoMethod *method);
+
MonoArray*
mono_runtime_get_main_args (void);
+2008-05-03 Robert Jordan <robertj@gmx.net>
+
+ * libtest.c, thunks.cs: tests for mono_method_get_unmanaged_thunk ().
+
+ * Makefile.am: add thunk.cs. link libtest with gmodule.
+
+ Code is contributed under MIT/X11 license.
+
2008-04-28 Mark Probst <mark.probst@gmail.com>
* generic-array-type.2.cs: Test case for type arguments in arrays.
bug-322722_patch_bx.2.cs \
bug-348522.2.cs \
bug-340662_bug.cs \
- bug-322722_dyn_method_throw.2.cs
+ bug-322722_dyn_method_throw.2.cs \
+ thunks.cs
if AMD64
TEST_CS_SRC = $(BASE_TEST_CS_SRC) async-exc-compilation.cs
noinst_LTLIBRARIES = libtest.la
-INCLUDES = $(GLIB_CFLAGS)
+INCLUDES = $(GLIB_CFLAGS) $(GMODULE_CFLAGS)
if PLATFORM_WIN32
# gcc-3.4.4 emits incorrect code when making indirect calls to stdcall functions using a tail call
libtest_la_LDFLAGS = -rpath `pwd`
endif
libtest_la_SOURCES = libtest.c
-libtest_la_LIBADD = $(GLIB_LIBS)
+libtest_la_LIBADD = $(GLIB_LIBS) $(GMODULE_LIBS)
CLEANFILES = $(TESTSI_CS) $(TESTSI_IL) $(STRESS_TESTS) *.dll *.stdout *.exe stest.dat
#include <stdlib.h>
#include <string.h>
#include <glib.h>
+#include <gmodule.h>
#include <errno.h>
#include <time.h>
+#include <math.h>
#ifdef WIN32
#include <windows.h>
#endif //NOT_YET
+
+
+/*
+ * mono_method_get_unmanaged_thunk tests
+ */
+
+/* thunks.cs:TestStruct */
+typedef struct _TestStruct {
+ int A;
+ double B;
+} TestStruct;
+
+/* Searches for mono symbols in all loaded modules */
+static gpointer
+lookup_mono_symbol (char *symbol_name)
+{
+ gpointer symbol;
+ if (g_module_symbol (g_module_open (NULL, G_MODULE_BIND_LAZY), symbol_name, &symbol))
+ return symbol;
+ else
+ return NULL;
+}
+
+/**
+ * test_method_thunk:
+ *
+ * @id: the method number
+ * @test_method_handle: MonoMethod* of the C# test method
+ * @create_object_method_handle: MonoMethod* of thunks.cs:Test.CreateObject
+ */
+STDCALL int
+test_method_thunk (int id, gpointer test_method_handle, gpointer create_object_method_handle)
+{
+ gpointer (*mono_method_get_unmanaged_thunk)(gpointer)
+ = lookup_mono_symbol ("mono_method_get_unmanaged_thunk");
+
+ gpointer (*mono_string_new_wrapper)(char *)
+ = lookup_mono_symbol ("mono_string_new_wrapper");
+
+ char* (*mono_string_to_utf8)(gpointer)
+ = lookup_mono_symbol ("mono_string_to_utf8");
+
+ gpointer test_method, ex = NULL;
+ gpointer (STDCALL *CreateObject)(gpointer*);
+
+
+ if (!mono_method_get_unmanaged_thunk)
+ return 1;
+
+ test_method = mono_method_get_unmanaged_thunk (test_method_handle);
+ if (!test_method)
+ return 2;
+
+ CreateObject = mono_method_get_unmanaged_thunk (create_object_method_handle);
+ if (!CreateObject)
+ return 3;
+
+
+ switch (id) {
+
+ case 0: {
+ /* thunks.cs:Test.Foo0 */
+ void (STDCALL *F)(gpointer*) = test_method;
+ F (&ex);
+ break;
+ }
+
+ case 1: {
+ /* thunks.cs:Test.Foo1 */
+ int (STDCALL *F)(gpointer*) = test_method;
+ if (F (&ex) != 42)
+ return 4;
+ break;
+ }
+
+ case 2: {
+ /* thunks.cs:Test.Foo2 */
+ gpointer (STDCALL *F)(gpointer, gpointer*) = test_method;
+ gpointer str = mono_string_new_wrapper ("foo");
+ if (str != F (str, &ex))
+ return 4;
+ break;
+ }
+
+ case 3: {
+ /* thunks.cs:Test.Foo3 */
+ gpointer (STDCALL *F)(gpointer, gpointer, gpointer*);
+ gpointer obj;
+ gpointer str;
+
+ F = test_method;
+ obj = CreateObject (&ex);
+ str = mono_string_new_wrapper ("bar");
+
+ if (str != F (obj, str, &ex))
+ return 4;
+ break;
+ }
+
+ case 4: {
+ /* thunks.cs:Test.Foo4 */
+ int (STDCALL *F)(gpointer, gpointer, int, gpointer*);
+ gpointer obj;
+ gpointer str;
+
+ F = test_method;
+ obj = CreateObject (&ex);
+ str = mono_string_new_wrapper ("bar");
+
+ if (42 != F (obj, str, 42, &ex))
+ return 4;
+
+ break;
+ }
+
+ case 5: {
+ /* thunks.cs:Test.Foo5 */
+ int (STDCALL *F)(gpointer, gpointer, int, gpointer*);
+ gpointer obj;
+ gpointer str;
+
+ F = test_method;
+ obj = CreateObject (&ex);
+ str = mono_string_new_wrapper ("bar");
+
+ F (obj, str, 42, &ex);
+ if (!ex)
+ return 4;
+
+ break;
+ }
+
+ case 6: {
+ /* thunks.cs:Test.Foo6 */
+ int (STDCALL *F)(gpointer, guint8, gint16, gint32, gint64, float, double,
+ gpointer, gpointer*);
+ gpointer obj;
+ gpointer str = mono_string_new_wrapper ("Foo6");
+ int res;
+
+ F = test_method;
+ obj = CreateObject (&ex);
+
+ res = F (obj, 254, 32700, -245378, 6789600, 3.1415, 3.1415, str, &ex);
+ if (ex)
+ return 4;
+
+ if (!res)
+ return 5;
+
+ break;
+ }
+
+ case 7: {
+ /* thunks.cs:Test.Foo7 */
+ gint64 (STDCALL *F)(gpointer*) = test_method;
+ if (F (&ex) != G_MAXINT64)
+ return 4;
+ break;
+ }
+
+ case 8: {
+ /* thunks.cs:Test.Foo8 */
+ void (STDCALL *F)(guint8*, gint16*, gint32*, gint64*, float*, double*,
+ gpointer*, gpointer*);
+
+ guint8 a1;
+ gint16 a2;
+ gint32 a3;
+ gint64 a4;
+ float a5;
+ double a6;
+ gpointer a7;
+
+ F = test_method;
+
+ F (&a1, &a2, &a3, &a4, &a5, &a6, &a7, &ex);
+ if (ex)
+ return 4;
+
+ 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), "Foo8") == 0))
+ return 5;
+
+ break;
+ }
+
+ case 9: {
+ /* thunks.cs:Test.Foo9 */
+ void (STDCALL *F)(guint8*, gint16*, gint32*, gint64*, float*, double*,
+ gpointer*, gpointer*);
+
+ guint8 a1;
+ gint16 a2;
+ gint32 a3;
+ gint64 a4;
+ float a5;
+ double a6;
+ gpointer a7;
+
+ F = test_method;
+
+ F (&a1, &a2, &a3, &a4, &a5, &a6, &a7, &ex);
+ if (!ex)
+ return 4;
+
+ break;
+ }
+
+ case 10: {
+ /* thunks.cs:Test.Foo10 */
+ int (STDCALL *F)(TestStruct, gpointer*);
+
+ TestStruct a1;
+ int res;
+
+ a1.A = 42;
+ a1.B = 3.1415;
+
+ F = test_method;
+
+ res = F (a1, &ex);
+ if (ex)
+ return 4;
+
+ if (!res)
+ return 5;
+
+ break;
+ }
+
+ case 11: {
+ /* thunks.cs:Test.Foo11 */
+ void (STDCALL *F)(TestStruct*, gpointer*);
+
+ TestStruct a1;
+
+ F = test_method;
+
+ F (&a1, &ex);
+ if (ex)
+ return 4;
+
+ if (!a1.A == 42)
+ return 5;
+
+ if (!fabs (a1.B - 3.1415) < 0.001)
+ return 6;
+
+ break;
+ }
+
+ case 12: {
+ /* thunks.cs:Test.Foo12 */
+ TestStruct (STDCALL *F)(gpointer*);
+
+ TestStruct a1;
+
+ F = test_method;
+
+ a1 = F (&ex);
+ if (ex)
+ return 4;
+
+ if (!a1.A == 42)
+ return 5;
+
+ if (!fabs (a1.B - 3.1415) < 0.001)
+ return 6;
+
+ break;
+ }
+
+ case 13: {
+ /* thunks.cs:TestStruct.Foo13 */
+ void (STDCALL *F)(TestStruct*, gpointer*);
+
+ TestStruct a1;
+ a1.A = 42;
+ a1.B = 3.1415;
+
+ F = test_method;
+
+ F (&a1, &ex);
+ if (ex)
+ return 4;
+
+ if (a1.A != 1)
+ return 5;
+
+ if (a1.B != 17)
+ return 6;
+
+ break;
+ }
+
+ default:
+ return 9;
+
+ }
+
+ return 0;
+}
--- /dev/null
+using System;
+using System.Runtime.InteropServices;
+
+public class Test
+{
+ [DllImport ("libtest")]
+ public static extern int test_method_thunk (int id, IntPtr testMethodHandle,
+ IntPtr createObjectHandle);
+
+ static int test_method_thunk (int id, Type type)
+ {
+ string name = String.Format ("Foo{0}", id);
+ return test_method_thunk (
+ id,
+ type.GetMethod (name).MethodHandle.Value,
+ type.GetMethod ("CreateObject").MethodHandle.Value);
+ }
+
+ public static int Main ()
+ {
+ const int MaxClassTests = 13;
+ const int MaxStructTests = 1;
+
+ // tests of class "Test"
+ for (int i = 0; i < MaxClassTests; i++) {
+ int res = test_method_thunk (i, typeof (Test));
+ if (res != 0)
+ return i*10 + res;
+ }
+
+ // tests of struct "TestStruct"
+ for (int i = 0; i < MaxStructTests; i++) {
+ int res = test_method_thunk (MaxClassTests + i, typeof (TestStruct));
+ if (res != 0)
+ return i*10 + res;
+ }
+
+ return 0;
+ }
+
+ public static object CreateObject ()
+ {
+ Test t = new Test ();
+ return t;
+ }
+
+ public static void Foo0 ()
+ {
+ }
+
+ public static int Foo1 ()
+ {
+ return 42;
+ }
+
+ public static string Foo2 (string s)
+ {
+ return s;
+ }
+
+ public string Foo3 (string a)
+ {
+ return a;
+ }
+
+ public int Foo4 (string a, int i)
+ {
+ return i;
+ }
+
+ public int Foo5 (string a, int i)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public bool Foo6 (byte a1, short a2, int a3, long a4, float a5, double a6, string a7)
+ {
+ return a1 == 254 &&
+ a2 == 32700 &&
+ a3 == -245378 &&
+ a4 == 6789600 &&
+ (Math.Abs (a5 - 3.1415) < 0.001) &&
+ (Math.Abs (a6 - 3.1415) < 0.001) &&
+ a7 == "Foo6";
+ }
+
+ public static long Foo7 ()
+ {
+ return Int64.MaxValue;
+ }
+
+ public static void Foo8 (ref byte a1, ref short a2, ref int a3, ref long a4, ref float a5, ref double a6, ref string a7)
+ {
+ a1 = 254;
+ a2 = 32700;
+ a3 = -245378;
+ a4 = 6789600;
+ a5 = 3.1415f;
+ a6 = 3.1415;
+ a7 = "Foo8";
+ }
+
+ public static void Foo9 (ref byte a1, ref short a2, ref int a3, ref long a4, ref float a5, ref double a6, ref string a7)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public static bool Foo10 (TestStruct s)
+ {
+ return s.A == 42 && Math.Abs (s.B - 3.1415) < 0.001;
+ }
+
+ public static void Foo11 (ref TestStruct s)
+ {
+ s.A = 42;
+ s.B = 3.1415;
+ }
+
+ public static TestStruct Foo12 ()
+ {
+ TestStruct s = new TestStruct ();
+ s.A = 42;
+ s.B = 3.1415;
+ return s;
+ }
+}
+
+
+public struct TestStruct
+{
+ public int A;
+ public double B;
+
+ public static TestStruct CreateObject ()
+ {
+ return new TestStruct ();
+ }
+
+ public void Foo13 ()
+ {
+ A = 1;
+ B = 17;
+ }
+}