Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
6327 lines (5356 sloc) 181 KB
/**
* \file
* Routines for marshaling complex types in P/Invoke methods.
*
* Author:
* Paolo Molaro (lupus@ximian.com)
*
* Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
* Copyright 2004-2009 Novell, Inc (http://www.novell.com)
* Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
*
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
#include "config.h"
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#include "object.h"
#include "loader.h"
#include "cil-coff.h"
#include "metadata/marshal.h"
#include "metadata/marshal-internals.h"
#include "metadata/marshal-ilgen.h"
#include "metadata/method-builder.h"
#include "metadata/method-builder-internals.h"
#include "metadata/tabledefs.h"
#include "metadata/exception.h"
#include "metadata/appdomain.h"
#include "mono/metadata/abi-details.h"
#include "mono/metadata/class-abi-details.h"
#include "mono/metadata/debug-helpers.h"
#include "mono/metadata/threads.h"
#include "mono/metadata/monitor.h"
#include "mono/metadata/class-init.h"
#include "mono/metadata/class-internals.h"
#include "mono/metadata/metadata-internals.h"
#include "mono/metadata/domain-internals.h"
#include "mono/metadata/gc-internals.h"
#include "mono/metadata/threads-types.h"
#include "mono/metadata/string-icalls.h"
#include "mono/metadata/attrdefs.h"
#include "mono/metadata/cominterop.h"
#include "mono/metadata/remoting.h"
#include "mono/metadata/reflection-internals.h"
#include "mono/metadata/threadpool.h"
#include "mono/metadata/handle.h"
#include "mono/metadata/object-internals.h"
#include "mono/metadata/custom-attrs-internals.h"
#include "mono/metadata/abi-details.h"
#include "mono/metadata/custom-attrs-internals.h"
#include "mono/utils/mono-counters.h"
#include "mono/utils/mono-tls.h"
#include "mono/utils/mono-memory-model.h"
#include "mono/utils/atomic.h"
#include <mono/utils/mono-threads.h>
#include <mono/utils/mono-threads-coop.h>
#include <mono/utils/mono-error-internals.h>
#include <string.h>
#include <errno.h>
/* #define DEBUG_RUNTIME_CODE */
#define OPDEF(a,b,c,d,e,f,g,h,i,j) \
a = i,
enum {
#include "mono/cil/opcode.def"
LAST = 0xff
};
#undef OPDEF
/*
* This mutex protects the various marshalling related caches in MonoImage
* and a few other data structures static to this file.
*
* The marshal lock is a non-recursive complex lock that sits below the domain lock in the
* runtime locking latice. Which means it can take simple locks suck as the image lock.
*/
#define mono_marshal_lock() mono_locks_coop_acquire (&marshal_mutex, MarshalLock)
#define mono_marshal_unlock() mono_locks_coop_release (&marshal_mutex, MarshalLock)
static MonoCoopMutex marshal_mutex;
static gboolean marshal_mutex_initialized;
static MonoNativeTlsKey last_error_tls_id;
static MonoNativeTlsKey load_type_info_tls_id;
static gboolean use_aot_wrappers;
static int class_marshal_info_count;
static MonoMarshalCallbacks *
get_marshal_cb (void);
static void
delegate_hash_table_add (MonoDelegateHandle d);
static void
delegate_hash_table_remove (MonoDelegate *d);
static void
mono_byvalarray_to_array (MonoArray *arr, gpointer native_arr, MonoClass *eltype, guint32 elnum);
static void
mono_array_to_byvalarray (gpointer native_arr, MonoArray *arr, MonoClass *eltype, guint32 elnum);
gpointer
mono_delegate_handle_to_ftnptr (MonoDelegateHandle delegate, MonoError *error);
MonoDelegateHandle
mono_ftnptr_to_delegate_handle (MonoClass *klass, gpointer ftn, MonoError *error);
/* Lazy class loading functions */
static GENERATE_GET_CLASS_WITH_CACHE (string_builder, "System.Text", "StringBuilder");
static GENERATE_TRY_GET_CLASS_WITH_CACHE (unmanaged_function_pointer_attribute, "System.Runtime.InteropServices", "UnmanagedFunctionPointerAttribute");
static MonoImage*
get_method_image (MonoMethod *method)
{
return m_class_get_image (method->klass);
}
#ifdef __cplusplus
template <typename T>
static void
register_icall (T func, const char *name, const char *sigstr, gboolean no_wrapper)
#else
static void
register_icall (gpointer func, const char *name, const char *sigstr, gboolean no_wrapper)
#endif
{
MonoMethodSignature *sig = mono_create_icall_signature (sigstr);
mono_register_jit_icall (func, name, sig, no_wrapper);
}
#ifdef __cplusplus
template <typename T>
static void
register_icall_no_wrapper (T func, const char *name, const char *sigstr)
#else
static void
register_icall_no_wrapper (gpointer func, const char *name, const char *sigstr)
#endif
{
MonoMethodSignature *sig = mono_create_icall_signature (sigstr);
mono_register_jit_icall (func, name, sig, TRUE);
}
MonoMethodSignature*
mono_signature_no_pinvoke (MonoMethod *method)
{
MonoMethodSignature *sig = mono_method_signature (method);
if (sig->pinvoke) {
sig = mono_metadata_signature_dup_full (get_method_image (method), sig);
sig->pinvoke = FALSE;
}
return sig;
}
void
mono_marshal_init_tls (void)
{
mono_native_tls_alloc (&last_error_tls_id, NULL);
mono_native_tls_alloc (&load_type_info_tls_id, NULL);
}
MonoObject*
mono_object_isinst_icall (MonoObject *obj, MonoClass *klass)
{
if (!klass)
return NULL;
/* This is called from stelemref so it is expected to succeed */
/* Fastpath */
if (mono_class_is_interface (klass)) {
MonoVTable *vt = obj->vtable;
if (!m_class_is_inited (klass))
mono_class_init (klass);
if (MONO_VTABLE_IMPLEMENTS_INTERFACE (vt, m_class_get_interface_id (klass)))
return obj;
}
ERROR_DECL (error);
MonoObject *result = mono_object_isinst_checked (obj, klass, error);
mono_error_set_pending_exception (error);
return result;
}
MonoString*
ves_icall_mono_string_from_utf16 (const gunichar2 *data)
{
ERROR_DECL (error);
MonoString *result = mono_string_from_utf16_checked (data, error);
mono_error_set_pending_exception (error);
return result;
}
char*
ves_icall_mono_string_to_utf8 (MonoString *str)
{
ERROR_DECL (error);
char *result = mono_string_to_utf8_checked (str, error);
mono_error_set_pending_exception (error);
return result;
}
MonoString*
ves_icall_string_new_wrapper (const char *text)
{
if (text) {
ERROR_DECL (error);
MonoString *res = mono_string_new_checked (mono_domain_get (), text, error);
mono_error_set_pending_exception (error);
return res;
}
return NULL;
}
void
mono_marshal_init (void)
{
static gboolean module_initialized = FALSE;
if (!module_initialized) {
module_initialized = TRUE;
mono_coop_mutex_init_recursive (&marshal_mutex);
marshal_mutex_initialized = TRUE;
register_icall (mono_marshal_string_to_utf16, "mono_marshal_string_to_utf16", "ptr obj", FALSE);
register_icall (mono_marshal_string_to_utf16_copy, "mono_marshal_string_to_utf16_copy", "ptr obj", FALSE);
register_icall (mono_string_to_utf16, "mono_string_to_utf16", "ptr obj", FALSE);
register_icall (ves_icall_mono_string_from_utf16, "ves_icall_mono_string_from_utf16", "obj ptr", FALSE);
register_icall (mono_string_from_byvalstr, "mono_string_from_byvalstr", "obj ptr int", FALSE);
register_icall (mono_string_from_byvalwstr, "mono_string_from_byvalwstr", "obj ptr int", FALSE);
register_icall (mono_string_new_wrapper, "mono_string_new_wrapper", "obj ptr", FALSE);
register_icall (ves_icall_string_new_wrapper, "ves_icall_string_new_wrapper", "obj ptr", FALSE);
register_icall (mono_string_new_len_wrapper, "mono_string_new_len_wrapper", "obj ptr int", FALSE);
register_icall (ves_icall_mono_string_to_utf8, "ves_icall_mono_string_to_utf8", "ptr obj", FALSE);
register_icall (mono_string_to_utf8str, "mono_string_to_utf8str", "ptr obj", FALSE);
register_icall (mono_string_to_ansibstr, "mono_string_to_ansibstr", "ptr object", FALSE);
register_icall (mono_string_builder_to_utf8, "mono_string_builder_to_utf8", "ptr object", FALSE);
register_icall (mono_string_builder_to_utf16, "mono_string_builder_to_utf16", "ptr object", FALSE);
register_icall (mono_array_to_savearray, "mono_array_to_savearray", "ptr object", FALSE);
register_icall (mono_array_to_lparray, "mono_array_to_lparray", "ptr object", FALSE);
register_icall (mono_free_lparray, "mono_free_lparray", "void object ptr", FALSE);
register_icall (mono_byvalarray_to_array, "mono_byvalarray_to_array", "void object ptr ptr int32", FALSE);
register_icall (mono_byvalarray_to_byte_array, "mono_byvalarray_to_byte_array", "void object ptr int32", FALSE);
register_icall (mono_array_to_byvalarray, "mono_array_to_byvalarray", "void ptr object ptr int32", FALSE);
register_icall (mono_array_to_byte_byvalarray, "mono_array_to_byte_byvalarray", "void ptr object int32", FALSE);
register_icall (mono_delegate_to_ftnptr, "mono_delegate_to_ftnptr", "ptr object", FALSE);
register_icall (mono_ftnptr_to_delegate, "mono_ftnptr_to_delegate", "object ptr ptr", FALSE);
register_icall (mono_marshal_asany, "mono_marshal_asany", "ptr object int32 int32", FALSE);
register_icall (mono_marshal_free_asany, "mono_marshal_free_asany", "void object ptr int32 int32", FALSE);
register_icall (ves_icall_marshal_alloc, "ves_icall_marshal_alloc", "ptr ptr", FALSE);
register_icall (mono_marshal_free, "mono_marshal_free", "void ptr", FALSE);
register_icall (mono_marshal_set_last_error, "mono_marshal_set_last_error", "void", TRUE);
register_icall (mono_marshal_set_last_error_windows, "mono_marshal_set_last_error_windows", "void int32", TRUE);
register_icall (mono_string_utf8_to_builder, "mono_string_utf8_to_builder", "void ptr ptr", FALSE);
register_icall (mono_string_utf8_to_builder2, "mono_string_utf8_to_builder2", "object ptr", FALSE);
register_icall (mono_string_utf16_to_builder, "mono_string_utf16_to_builder", "void ptr ptr", FALSE);
register_icall (mono_string_utf16_to_builder2, "mono_string_utf16_to_builder2", "object ptr", FALSE);
register_icall (mono_marshal_free_array, "mono_marshal_free_array", "void ptr int32", FALSE);
register_icall (mono_string_to_byvalstr, "mono_string_to_byvalstr", "void ptr ptr int32", FALSE);
register_icall (mono_string_to_byvalwstr, "mono_string_to_byvalwstr", "void ptr ptr int32", FALSE);
register_icall (g_free, "g_free", "void ptr", FALSE);
register_icall_no_wrapper (mono_object_isinst_icall, "mono_object_isinst_icall", "object object ptr");
register_icall (mono_struct_delete_old, "mono_struct_delete_old", "void ptr ptr", FALSE);
register_icall (mono_delegate_begin_invoke, "mono_delegate_begin_invoke", "object object ptr", FALSE);
register_icall (mono_delegate_end_invoke, "mono_delegate_end_invoke", "object object ptr", FALSE);
register_icall (mono_gc_wbarrier_generic_nostore, "wb_generic", "void ptr", FALSE);
register_icall (mono_gchandle_get_target, "mono_gchandle_get_target", "object int32", TRUE);
register_icall (mono_marshal_isinst_with_cache, "mono_marshal_isinst_with_cache", "object object ptr ptr", FALSE);
register_icall (mono_threads_enter_gc_safe_region_unbalanced, "mono_threads_enter_gc_safe_region_unbalanced", "ptr ptr", TRUE);
register_icall (mono_threads_exit_gc_safe_region_unbalanced, "mono_threads_exit_gc_safe_region_unbalanced", "void ptr ptr", TRUE);
register_icall (mono_threads_enter_gc_unsafe_region_unbalanced, "mono_threads_enter_gc_unsafe_region_unbalanced", "ptr ptr", TRUE);
register_icall (mono_threads_exit_gc_unsafe_region_unbalanced, "mono_threads_exit_gc_unsafe_region_unbalanced", "void ptr ptr", TRUE);
register_icall (mono_threads_attach_coop, "mono_threads_attach_coop", "ptr ptr ptr", TRUE);
register_icall (mono_threads_detach_coop, "mono_threads_detach_coop", "void ptr ptr", TRUE);
register_icall (mono_icall_start, "mono_icall_start", "ptr ptr ptr", TRUE);
register_icall (mono_icall_end, "mono_icall_end", "void ptr ptr ptr", TRUE);
register_icall (mono_icall_handle_new, "mono_icall_handle_new", "ptr ptr", TRUE);
register_icall (mono_icall_handle_new_interior, "mono_icall_handle_new_interior", "ptr ptr", TRUE);
register_icall (mono_marshal_get_type_object, "mono_marshal_get_type_object", "object ptr", TRUE);
mono_cominterop_init ();
mono_remoting_init ();
mono_counters_register ("MonoClass::class_marshal_info_count count",
MONO_COUNTER_METADATA | MONO_COUNTER_INT, &class_marshal_info_count);
}
}
void
mono_marshal_cleanup (void)
{
mono_cominterop_cleanup ();
mono_native_tls_free (load_type_info_tls_id);
mono_native_tls_free (last_error_tls_id);
mono_coop_mutex_destroy (&marshal_mutex);
marshal_mutex_initialized = FALSE;
}
void
mono_marshal_lock_internal (void)
{
mono_marshal_lock ();
}
void
mono_marshal_unlock_internal (void)
{
mono_marshal_unlock ();
}
/* This is a JIT icall, it sets the pending exception and return NULL on error */
gpointer
mono_delegate_to_ftnptr (MonoDelegate *delegate_raw)
{
HANDLE_FUNCTION_ENTER ();
ERROR_DECL (error);
MONO_HANDLE_DCL (MonoDelegate, delegate);
gpointer result = mono_delegate_handle_to_ftnptr (delegate, error);
mono_error_set_pending_exception (error);
HANDLE_FUNCTION_RETURN_VAL (result);
}
gpointer
mono_delegate_handle_to_ftnptr (MonoDelegateHandle delegate, MonoError *error)
{
HANDLE_FUNCTION_ENTER ();
gpointer result = NULL;
error_init (error);
MonoMethod *method, *wrapper;
MonoClass *klass;
uint32_t target_handle = 0;
if (MONO_HANDLE_IS_NULL (delegate))
goto leave;
if (MONO_HANDLE_GETVAL (delegate, delegate_trampoline)) {
result = MONO_HANDLE_GETVAL (delegate, delegate_trampoline);
goto leave;
}
klass = mono_handle_class (delegate);
g_assert (m_class_is_delegate (klass));
method = MONO_HANDLE_GETVAL (delegate, method);
if (MONO_HANDLE_GETVAL (delegate, method_is_virtual)) {
MonoObjectHandle delegate_target = MONO_HANDLE_NEW_GET (MonoObject, delegate, target);
method = mono_object_handle_get_virtual_method (delegate_target, method, error);
goto_if_nok (error, leave);
}
if (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) {
const char *exc_class, *exc_arg;
gpointer ftnptr;
ftnptr = mono_lookup_pinvoke_call (method, &exc_class, &exc_arg);
if (!ftnptr) {
g_assert (exc_class);
mono_error_set_generic_error (error, "System", exc_class, "%s", exc_arg);
goto leave;
}
result = ftnptr;
goto leave;
}
MonoObjectHandle delegate_target;
delegate_target = MONO_HANDLE_NEW_GET (MonoObject, delegate, target);
if (!MONO_HANDLE_IS_NULL (delegate_target)) {
/* Produce a location which can be embedded in JITted code */
target_handle = mono_gchandle_new_weakref_from_handle (delegate_target);
}
wrapper = mono_marshal_get_managed_wrapper (method, klass, target_handle, error);
goto_if_nok (error, leave);
MONO_HANDLE_SETVAL (delegate, delegate_trampoline, gpointer, mono_compile_method_checked (wrapper, error));
goto_if_nok (error, leave);
// Add the delegate to the delegate hash table
delegate_hash_table_add (delegate);
/* when the object is collected, collect the dynamic method, too */
mono_object_register_finalizer ((MonoObject*) MONO_HANDLE_RAW (delegate));
result = MONO_HANDLE_GETVAL (delegate, delegate_trampoline);
leave:
if (!is_ok (error) && target_handle != 0)
mono_gchandle_free (target_handle);
HANDLE_FUNCTION_RETURN_VAL (result);
}
/*
* this hash table maps from a delegate trampoline object to a weak reference
* of the delegate. As an optimizations with a non-moving GC we store the
* object pointer itself, otherwise we use a GC handle.
*/
static GHashTable *delegate_hash_table;
static GHashTable *
delegate_hash_table_new (void) {
return g_hash_table_new (NULL, NULL);
}
static void
delegate_hash_table_remove (MonoDelegate *d)
{
guint32 gchandle = 0;
mono_marshal_lock ();
if (delegate_hash_table == NULL)
delegate_hash_table = delegate_hash_table_new ();
if (mono_gc_is_moving ())
gchandle = GPOINTER_TO_UINT (g_hash_table_lookup (delegate_hash_table, d->delegate_trampoline));
g_hash_table_remove (delegate_hash_table, d->delegate_trampoline);
mono_marshal_unlock ();
if (gchandle && mono_gc_is_moving ())
mono_gchandle_free (gchandle);
}
static void
delegate_hash_table_add (MonoDelegateHandle d)
{
guint32 gchandle;
guint32 old_gchandle;
mono_marshal_lock ();
if (delegate_hash_table == NULL)
delegate_hash_table = delegate_hash_table_new ();
gpointer delegate_trampoline = MONO_HANDLE_GETVAL (d, delegate_trampoline);
if (mono_gc_is_moving ()) {
gchandle = mono_gchandle_new_weakref_from_handle (MONO_HANDLE_CAST (MonoObject, d));
old_gchandle = GPOINTER_TO_UINT (g_hash_table_lookup (delegate_hash_table, delegate_trampoline));
g_hash_table_insert (delegate_hash_table, delegate_trampoline, GUINT_TO_POINTER (gchandle));
if (old_gchandle)
mono_gchandle_free (old_gchandle);
} else {
g_hash_table_insert (delegate_hash_table, delegate_trampoline, MONO_HANDLE_RAW (d));
}
mono_marshal_unlock ();
}
/*
* mono_marshal_use_aot_wrappers:
*
* Instructs this module to use AOT compatible wrappers.
*/
void
mono_marshal_use_aot_wrappers (gboolean use)
{
use_aot_wrappers = use;
}
static void
parse_unmanaged_function_pointer_attr (MonoClass *klass, MonoMethodPInvoke *piinfo)
{
ERROR_DECL (error);
MonoCustomAttrInfo *cinfo;
MonoReflectionUnmanagedFunctionPointerAttribute *attr;
/* The attribute is only available in Net 2.0 */
if (mono_class_try_get_unmanaged_function_pointer_attribute_class ()) {
/*
* The pinvoke attributes are stored in a real custom attribute so we have to
* construct it.
*/
cinfo = mono_custom_attrs_from_class_checked (klass, error);
if (!mono_error_ok (error)) {
g_warning ("Could not load UnmanagedFunctionPointerAttribute due to %s", mono_error_get_message (error));
mono_error_cleanup (error);
}
if (cinfo && !mono_runtime_get_no_exec ()) {
attr = (MonoReflectionUnmanagedFunctionPointerAttribute*)mono_custom_attrs_get_attr_checked (cinfo, mono_class_try_get_unmanaged_function_pointer_attribute_class (), error);
if (attr) {
piinfo->piflags = (attr->call_conv << 8) | (attr->charset ? (attr->charset - 1) * 2 : 1) | attr->set_last_error;
} else {
if (!mono_error_ok (error)) {
g_warning ("Could not load UnmanagedFunctionPointerAttribute due to %s", mono_error_get_message (error));
mono_error_cleanup (error);
}
}
if (!cinfo->cached)
mono_custom_attrs_free (cinfo);
}
}
}
/* This is a JIT icall, it sets the pending exception and returns NULL on error */
MonoDelegate*
mono_ftnptr_to_delegate (MonoClass *klass, gpointer ftn)
{
HANDLE_FUNCTION_ENTER ();
ERROR_DECL (error);
MonoDelegateHandle result = mono_ftnptr_to_delegate_handle (klass, ftn, error);
mono_error_set_pending_exception (error);
HANDLE_FUNCTION_RETURN_OBJ (result);
}
MonoDelegateHandle
mono_ftnptr_to_delegate_handle (MonoClass *klass, gpointer ftn, MonoError *error)
{
HANDLE_FUNCTION_ENTER ();
error_init (error);
guint32 gchandle;
MonoDelegateHandle d = MONO_HANDLE_NEW (MonoDelegate, NULL);
if (ftn == NULL)
goto leave;
mono_marshal_lock ();
if (delegate_hash_table == NULL)
delegate_hash_table = delegate_hash_table_new ();
if (mono_gc_is_moving ()) {
gchandle = GPOINTER_TO_UINT (g_hash_table_lookup (delegate_hash_table, ftn));
mono_marshal_unlock ();
if (gchandle)
MONO_HANDLE_ASSIGN (d, MONO_HANDLE_CAST (MonoDelegate, mono_gchandle_get_target_handle (gchandle)));
} else {
MONO_HANDLE_ASSIGN (d, MONO_HANDLE_NEW (MonoDelegate, (MonoDelegate*)g_hash_table_lookup (delegate_hash_table, ftn)));
mono_marshal_unlock ();
}
if (MONO_HANDLE_IS_NULL (d)) {
/* This is a native function, so construct a delegate for it */
MonoMethodSignature *sig;
MonoMethod *wrapper;
MonoMarshalSpec **mspecs;
MonoMethod *invoke = mono_get_delegate_invoke (klass);
MonoMethodPInvoke piinfo;
MonoObjectHandle this_obj;
int i;
if (use_aot_wrappers) {
wrapper = mono_marshal_get_native_func_wrapper_aot (klass);
this_obj = MONO_HANDLE_NEW (MonoObject, mono_value_box_checked (mono_domain_get (), mono_defaults.int_class, &ftn, error));
goto_if_nok (error, leave);
} else {
memset (&piinfo, 0, sizeof (piinfo));
parse_unmanaged_function_pointer_attr (klass, &piinfo);
mspecs = g_new0 (MonoMarshalSpec*, mono_method_signature (invoke)->param_count + 1);
mono_method_get_marshal_info (invoke, mspecs);
/* Freed below so don't alloc from mempool */
sig = mono_metadata_signature_dup (mono_method_signature (invoke));
sig->hasthis = 0;
wrapper = mono_marshal_get_native_func_wrapper (m_class_get_image (klass), sig, &piinfo, mspecs, ftn);
this_obj = MONO_HANDLE_NEW (MonoObject, NULL);
for (i = mono_method_signature (invoke)->param_count; i >= 0; i--)
if (mspecs [i])
mono_metadata_free_marshal_spec (mspecs [i]);
g_free (mspecs);
g_free (sig);
}
MONO_HANDLE_ASSIGN (d, mono_object_new_handle (mono_domain_get (), klass, error));
goto_if_nok (error, leave);
gpointer compiled_ptr = mono_compile_method_checked (wrapper, error);
goto_if_nok (error, leave);
mono_delegate_ctor_with_method (MONO_HANDLE_CAST (MonoObject, d), this_obj, compiled_ptr, wrapper, error);
goto_if_nok (error, leave);
}
g_assert (!MONO_HANDLE_IS_NULL (d));
if (MONO_HANDLE_DOMAIN (d) != mono_domain_get ())
mono_error_set_not_supported (error, "Delegates cannot be marshalled from native code into a domain other than their home domain");
leave:
HANDLE_FUNCTION_RETURN_REF (MonoDelegate, d);
}
void
mono_delegate_free_ftnptr (MonoDelegate *delegate)
{
MonoJitInfo *ji;
void *ptr;
delegate_hash_table_remove (delegate);
ptr = (gpointer)mono_atomic_xchg_ptr (&delegate->delegate_trampoline, NULL);
if (!delegate->target) {
/* The wrapper method is shared between delegates -> no need to free it */
return;
}
if (ptr) {
uint32_t gchandle;
void **method_data;
MonoMethod *method;
ji = mono_jit_info_table_find (mono_domain_get (), mono_get_addr_from_ftnptr (ptr));
/* FIXME we leak wrapper with the interpreter */
if (!ji)
return;
method = mono_jit_info_get_method (ji);
method_data = (void **)((MonoMethodWrapper*)method)->method_data;
/*the target gchandle is the first entry after size and the wrapper itself.*/
gchandle = GPOINTER_TO_UINT (method_data [2]);
if (gchandle)
mono_gchandle_free (gchandle);
mono_runtime_free_method (mono_object_domain (delegate), method);
}
}
/* This is a JIT icall, it sets the pending exception and returns NULL on error */
MonoString *
mono_string_from_byvalstr (const char *data, int max_len)
{
ERROR_DECL (error);
MonoDomain *domain = mono_domain_get ();
int len = 0;
if (!data)
return NULL;
while (len < max_len - 1 && data [len])
len++;
MonoString *result = mono_string_new_len_checked (domain, data, len, error);
mono_error_set_pending_exception (error);
return result;
}
/* This is a JIT icall, it sets the pending exception and return NULL on error */
MonoString *
mono_string_from_byvalwstr (gunichar2 *data, int max_len)
{
ERROR_DECL (error);
MonoString *res = NULL;
MonoDomain *domain = mono_domain_get ();
int len = 0;
if (!data)
return NULL;
while (data [len]) len++;
res = mono_string_new_utf16_checked (domain, data, MIN (len, max_len), error);
if (!mono_error_ok (error)) {
mono_error_set_pending_exception (error);
return NULL;
}
return res;
}
gpointer
mono_array_to_savearray (MonoArray *array)
{
if (!array)
return NULL;
g_assert_not_reached ();
return NULL;
}
gpointer
mono_array_to_lparray (MonoArray *array)
{
#ifndef DISABLE_COM
gpointer *nativeArray = NULL;
int nativeArraySize = 0;
int i = 0;
MonoClass *klass;
ERROR_DECL (error);
#endif
if (!array)
return NULL;
#ifndef DISABLE_COM
error_init (error);
klass = array->obj.vtable->klass;
MonoClass *klass_element_class = m_class_get_element_class (klass);
switch (m_class_get_byval_arg (klass_element_class)->type) {
case MONO_TYPE_VOID:
g_assert_not_reached ();
break;
case MONO_TYPE_CLASS:
nativeArraySize = array->max_length;
nativeArray = (void **)g_malloc (sizeof(gpointer) * nativeArraySize);
for(i = 0; i < nativeArraySize; ++i) {
nativeArray[i] = mono_cominterop_get_com_interface (((MonoObject **)array->vector)[i], klass_element_class, error);
if (mono_error_set_pending_exception (error))
break;
}
return nativeArray;
case MONO_TYPE_U1:
case MONO_TYPE_BOOLEAN:
case MONO_TYPE_I1:
case MONO_TYPE_U2:
case MONO_TYPE_CHAR:
case MONO_TYPE_I2:
case MONO_TYPE_I:
case MONO_TYPE_U:
case MONO_TYPE_I4:
case MONO_TYPE_U4:
case MONO_TYPE_U8:
case MONO_TYPE_I8:
case MONO_TYPE_R4:
case MONO_TYPE_R8:
case MONO_TYPE_VALUETYPE:
case MONO_TYPE_PTR:
/* nothing to do */
break;
case MONO_TYPE_GENERICINST:
case MONO_TYPE_OBJECT:
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY:
case MONO_TYPE_STRING:
default:
g_warning ("type 0x%x not handled", m_class_get_byval_arg (klass_element_class)->type);
g_assert_not_reached ();
}
#endif
return array->vector;
}
void
mono_free_lparray (MonoArray *array, gpointer* nativeArray)
{
#ifndef DISABLE_COM
MonoClass *klass;
if (!array)
return;
if (!nativeArray)
return;
klass = array->obj.vtable->klass;
if (m_class_get_byval_arg (m_class_get_element_class (klass))->type == MONO_TYPE_CLASS)
g_free (nativeArray);
#endif
}
void
mono_byvalarray_to_array (MonoArray *arr, gpointer native_arr, MonoClass *elclass, guint32 elnum)
{
g_assert (m_class_get_element_class (mono_object_class (&arr->obj)) == mono_defaults.char_class);
if (elclass == mono_defaults.byte_class) {
GError *gerror = NULL;
gunichar2 *ut;
glong items_written;
ut = g_utf8_to_utf16 ((const gchar *)native_arr, elnum, NULL, &items_written, &gerror);
if (!gerror) {
memcpy (mono_array_addr (arr, gunichar2, 0), ut, items_written * sizeof (gunichar2));
g_free (ut);
}
else
g_error_free (gerror);
}
else
g_assert_not_reached ();
}
void
mono_byvalarray_to_byte_array (MonoArray *arr, gpointer native_arr, guint32 elnum)
{
mono_byvalarray_to_array (arr, native_arr, mono_defaults.byte_class, elnum);
}
/* This is a JIT icall, it sets the pending exception and returns on error */
static void
mono_array_to_byvalarray (gpointer native_arr, MonoArray *arr, MonoClass *elclass, guint32 elnum)
{
g_assert (m_class_get_element_class (mono_object_class (&arr->obj)) == mono_defaults.char_class);
if (elclass == mono_defaults.byte_class) {
char *as;
GError *gerror = NULL;
as = g_utf16_to_utf8 (mono_array_addr (arr, gunichar2, 0), mono_array_length (arr), NULL, NULL, &gerror);
if (gerror) {
ERROR_DECL (error);
mono_error_set_argument (error, "string", gerror->message);
mono_error_set_pending_exception (error);
g_error_free (gerror);
return;
}
memcpy (native_arr, as, MIN (strlen (as), elnum));
g_free (as);
} else {
g_assert_not_reached ();
}
}
void
mono_array_to_byte_byvalarray (gpointer native_arr, MonoArray *arr, guint32 elnum)
{
mono_array_to_byvalarray (native_arr, arr, mono_defaults.byte_class, elnum);
}
static MonoStringBuilder *
mono_string_builder_new (int starting_string_length)
{
static MonoClass *string_builder_class;
static MonoMethod *sb_ctor;
static void *args [1];
ERROR_DECL (error);
int initial_len = starting_string_length;
if (initial_len < 0)
initial_len = 0;
if (!sb_ctor) {
MonoMethodDesc *desc;
MonoMethod *m;
string_builder_class = mono_class_get_string_builder_class ();
g_assert (string_builder_class);
desc = mono_method_desc_new (":.ctor(int)", FALSE);
m = mono_method_desc_search_in_class (desc, string_builder_class);
g_assert (m);
mono_method_desc_free (desc);
mono_memory_barrier ();
sb_ctor = m;
}
// We make a new array in the _to_builder function, so this
// array will always be garbage collected.
args [0] = &initial_len;
MonoStringBuilder *sb = (MonoStringBuilder*)mono_object_new_checked (mono_domain_get (), string_builder_class, error);
mono_error_assert_ok (error);
MonoObject *exc;
mono_runtime_try_invoke (sb_ctor, sb, args, &exc, error);
g_assert (exc == NULL);
mono_error_assert_ok (error);
g_assert (sb->chunkChars->max_length >= initial_len);
return sb;
}
static void
mono_string_utf16_to_builder_copy (MonoStringBuilder *sb, const gunichar2 *text, size_t string_len)
{
gunichar2 *charDst = (gunichar2 *)sb->chunkChars->vector;
memcpy (charDst, text, sizeof (gunichar2) * string_len);
sb->chunkLength = string_len;
}
MonoStringBuilder *
mono_string_utf16_to_builder2 (const gunichar2 *text)
{
if (!text)
return NULL;
int len;
for (len = 0; text [len] != 0; ++len);
MonoStringBuilder *sb = mono_string_builder_new (len);
mono_string_utf16_to_builder (sb, text);
return sb;
}
void
mono_string_utf8_to_builder (MonoStringBuilder *sb, const char *text)
{
if (!sb || !text)
return;
GError *gerror = NULL;
glong copied;
gunichar2* ut = g_utf8_to_utf16 (text, strlen (text), NULL, &copied, &gerror);
int capacity = mono_string_builder_capacity (sb);
if (copied > capacity)
copied = capacity;
if (!gerror) {
MONO_OBJECT_SETREF (sb, chunkPrevious, NULL);
mono_string_utf16_to_builder_copy (sb, ut, copied);
} else
g_error_free (gerror);
g_free (ut);
}
MonoStringBuilder *
mono_string_utf8_to_builder2 (const char *text)
{
if (!text)
return NULL;
int len = strlen (text);
MonoStringBuilder *sb = mono_string_builder_new (len);
mono_string_utf8_to_builder (sb, text);
return sb;
}
void
mono_string_utf16_to_builder (MonoStringBuilder *sb, const gunichar2 *text)
{
if (!sb || !text)
return;
guint32 len;
for (len = 0; text [len] != 0; ++len);
if (len > mono_string_builder_capacity (sb))
len = mono_string_builder_capacity (sb);
mono_string_utf16_to_builder_copy (sb, text, len);
}
/**
* mono_string_builder_to_utf8:
* \param sb the string builder
*
* Converts to utf8 the contents of the \c MonoStringBuilder .
*
* \returns a utf8 string with the contents of the \c StringBuilder .
*
* The return value must be released with mono_marshal_free.
*
* This is a JIT icall, it sets the pending exception and returns NULL on error.
*/
gchar*
mono_string_builder_to_utf8 (MonoStringBuilder *sb)
{
ERROR_DECL (error);
GError *gerror = NULL;
glong byte_count;
if (!sb)
return NULL;
gunichar2 *str_utf16 = mono_string_builder_to_utf16 (sb);
guint str_len = mono_string_builder_string_length (sb);
gchar *tmp = g_utf16_to_utf8 (str_utf16, str_len, NULL, &byte_count, &gerror);
if (gerror) {
g_error_free (gerror);
mono_marshal_free (str_utf16);
mono_error_set_execution_engine (error, "Failed to convert StringBuilder from utf16 to utf8");
mono_error_set_pending_exception (error);
return NULL;
} else {
guint len = mono_string_builder_capacity (sb) + 1;
gchar *res = (gchar *)mono_marshal_alloc (MAX (byte_count+1, len * sizeof (gchar)), error);
if (!mono_error_ok (error)) {
mono_marshal_free (str_utf16);
g_free (tmp);
mono_error_set_pending_exception (error);
return NULL;
}
memcpy (res, tmp, byte_count);
res[byte_count] = '\0';
mono_marshal_free (str_utf16);
g_free (tmp);
return res;
}
}
/**
* mono_string_builder_to_utf16:
* \param sb the string builder
*
* Converts to utf16 the contents of the \c MonoStringBuilder .
*
* Returns: a utf16 string with the contents of the \c StringBuilder .
*
* The return value must be released with mono_marshal_free.
*
* This is a JIT icall, it sets the pending exception and returns NULL on error.
*/
gunichar2*
mono_string_builder_to_utf16 (MonoStringBuilder *sb)
{
ERROR_DECL (error);
if (!sb)
return NULL;
g_assert (sb->chunkChars);
guint len = mono_string_builder_capacity (sb);
if (len == 0)
len = 1;
gunichar2 *str = (gunichar2 *)mono_marshal_alloc ((len + 1) * sizeof (gunichar2), error);
if (!mono_error_ok (error)) {
mono_error_set_pending_exception (error);
return NULL;
}
str[len] = '\0';
if (len == 0)
return str;
MonoStringBuilder* chunk = sb;
do {
if (chunk->chunkLength > 0) {
// Check that we will not overrun our boundaries.
gunichar2 *source = (gunichar2 *)chunk->chunkChars->vector;
g_assertf (chunk->chunkLength <= len, "A chunk in the StringBuilder had a length longer than expected from the offset.");
memcpy (str + chunk->chunkOffset, source, chunk->chunkLength * sizeof(gunichar2));
len -= chunk->chunkLength;
}
chunk = chunk->chunkPrevious;
} while (chunk != NULL);
return str;
}
#ifndef HOST_WIN32
/* This is a JIT icall, it sets the pending exception and returns NULL on error. */
gpointer
mono_string_to_utf8str (MonoString *s_raw)
{
HANDLE_FUNCTION_ENTER ();
ERROR_DECL (error);
MONO_HANDLE_DCL (MonoString, s);
gpointer result = mono_string_handle_to_utf8 (s, error);
mono_error_set_pending_exception (error);
HANDLE_FUNCTION_RETURN_VAL (result);
}
#else
// Win32 version uses CoTaskMemAlloc.
#endif
gpointer
mono_string_to_ansibstr (MonoString *string_obj)
{
g_error ("UnmanagedMarshal.BStr is not implemented.");
return NULL;
}
/**
* mono_string_to_byvalstr:
* \param dst Where to store the null-terminated utf8 decoded string.
* \param src the \c MonoString to copy.
* \param size the maximum number of bytes to copy.
*
* Copies the \c MonoString pointed to by \p src as a utf8 string
* into \p dst, it copies at most \p size bytes into the destination.
*/
void
mono_string_to_byvalstr (gpointer dst, MonoString *src, int size)
{
ERROR_DECL (error);
char *s;
int len;
g_assert (dst != NULL);
g_assert (size > 0);
memset (dst, 0, size);
if (!src)
return;
s = mono_string_to_utf8_checked (src, error);
if (mono_error_set_pending_exception (error))
return;
len = MIN (size, strlen (s));
if (len >= size)
len--;
memcpy (dst, s, len);
g_free (s);
}
/**
* mono_string_to_byvalwstr:
* \param dst Where to store the null-terminated utf16 decoded string.
* \param src the \c MonoString to copy.
* \param size the maximum number of wide characters to copy (each consumes 2 bytes)
*
* Copies the \c MonoString pointed to by \p src as a utf16 string into
* \p dst, it copies at most \p size bytes into the destination (including
* a terminating 16-bit zero terminator).
*/
void
mono_string_to_byvalwstr (gpointer dst, MonoString *src, int size)
{
int len;
g_assert (dst != NULL);
g_assert (size > 1);
if (!src) {
memset (dst, 0, size * 2);
return;
}
len = MIN (size, (mono_string_length (src)));
memcpy (dst, mono_string_chars (src), len * 2);
if (size <= mono_string_length (src))
len--;
*((gunichar2 *) dst + len) = 0;
}
/* this is an icall, it sets the pending exception and returns NULL on error */
MonoString*
mono_string_new_len_wrapper (const char *text, guint length)
{
ERROR_DECL (error);
MonoString *result = mono_string_new_len_checked (mono_domain_get (), text, length, error);
mono_error_set_pending_exception (error);
return result;
}
guint
mono_type_to_ldind (MonoType *type)
{
if (type->byref)
return CEE_LDIND_I;
handle_enum:
switch (type->type) {
case MONO_TYPE_I1:
return CEE_LDIND_I1;
case MONO_TYPE_U1:
case MONO_TYPE_BOOLEAN:
return CEE_LDIND_U1;
case MONO_TYPE_I2:
return CEE_LDIND_I2;
case MONO_TYPE_U2:
case MONO_TYPE_CHAR:
return CEE_LDIND_U2;
case MONO_TYPE_I4:
return CEE_LDIND_I4;
case MONO_TYPE_U4:
return CEE_LDIND_U4;
case MONO_TYPE_I:
case MONO_TYPE_U:
case MONO_TYPE_PTR:
case MONO_TYPE_FNPTR:
return CEE_LDIND_I;
case MONO_TYPE_CLASS:
case MONO_TYPE_STRING:
case MONO_TYPE_OBJECT:
case MONO_TYPE_SZARRAY:
case MONO_TYPE_ARRAY:
return CEE_LDIND_REF;
case MONO_TYPE_I8:
case MONO_TYPE_U8:
return CEE_LDIND_I8;
case MONO_TYPE_R4:
return CEE_LDIND_R4;
case MONO_TYPE_R8:
return CEE_LDIND_R8;
case MONO_TYPE_VALUETYPE:
if (m_class_is_enumtype (type->data.klass)) {
type = mono_class_enum_basetype (type->data.klass);
goto handle_enum;
}
return CEE_LDOBJ;
case MONO_TYPE_TYPEDBYREF:
return CEE_LDOBJ;
case MONO_TYPE_GENERICINST:
type = m_class_get_byval_arg (type->data.generic_class->container_class);
goto handle_enum;
default:
g_error ("unknown type 0x%02x in type_to_ldind", type->type);
}
return -1;
}
guint
mono_type_to_stind (MonoType *type)
{
if (type->byref)
return MONO_TYPE_IS_REFERENCE (type) ? CEE_STIND_REF : CEE_STIND_I;
handle_enum:
switch (type->type) {
case MONO_TYPE_I1:
case MONO_TYPE_U1:
case MONO_TYPE_BOOLEAN:
return CEE_STIND_I1;
case MONO_TYPE_I2:
case MONO_TYPE_U2:
case MONO_TYPE_CHAR:
return CEE_STIND_I2;
case MONO_TYPE_I4:
case MONO_TYPE_U4:
return CEE_STIND_I4;
case MONO_TYPE_I:
case MONO_TYPE_U:
case MONO_TYPE_PTR:
case MONO_TYPE_FNPTR:
return CEE_STIND_I;
case MONO_TYPE_CLASS:
case MONO_TYPE_STRING:
case MONO_TYPE_OBJECT:
case MONO_TYPE_SZARRAY:
case MONO_TYPE_ARRAY:
return CEE_STIND_REF;
case MONO_TYPE_I8:
case MONO_TYPE_U8:
return CEE_STIND_I8;
case MONO_TYPE_R4:
return CEE_STIND_R4;
case MONO_TYPE_R8:
return CEE_STIND_R8;
case MONO_TYPE_VALUETYPE:
if (m_class_is_enumtype (type->data.klass)) {
type = mono_class_enum_basetype (type->data.klass);
goto handle_enum;
}
return CEE_STOBJ;
case MONO_TYPE_TYPEDBYREF:
return CEE_STOBJ;
case MONO_TYPE_GENERICINST:
type = m_class_get_byval_arg (type->data.generic_class->container_class);
goto handle_enum;
default:
g_error ("unknown type 0x%02x in type_to_stind", type->type);
}
return -1;
}
/* This is a JIT icall, it sets the pending exception and returns NULL on error. */
MonoAsyncResult *
mono_delegate_begin_invoke (MonoDelegate *delegate, gpointer *params)
{
ERROR_DECL (error);
MonoMulticastDelegate *mcast_delegate;
MonoClass *klass;
MonoMethod *method;
g_assert (delegate);
mcast_delegate = (MonoMulticastDelegate *) delegate;
if (mcast_delegate->delegates != NULL) {
mono_error_set_argument (error, NULL, "The delegate must have only one target");
mono_error_set_pending_exception (error);
return NULL;
}
#ifndef DISABLE_REMOTING
if (delegate->target && mono_object_is_transparent_proxy (delegate->target)) {
MonoTransparentProxy* tp = (MonoTransparentProxy *)delegate->target;
if (!mono_class_is_contextbound (tp->remote_class->proxy_class) || tp->rp->context != (MonoObject *) mono_context_get ()) {
/* If the target is a proxy, make a direct call. Is proxy's work
// to make the call asynchronous.
*/
MonoMethodMessage *msg;
MonoDelegate *async_callback;
MonoObject *state;
MonoAsyncResult *ares;
MonoObject *exc;
MonoArray *out_args;
method = delegate->method;
msg = mono_method_call_message_new (mono_marshal_method_from_wrapper (method), params, NULL, &async_callback, &state, error);
if (mono_error_set_pending_exception (error))
return NULL;
ares = mono_async_result_new (mono_domain_get (), NULL, state, NULL, NULL, error);
if (mono_error_set_pending_exception (error))
return NULL;
MONO_OBJECT_SETREF (ares, async_delegate, (MonoObject *)delegate);
MONO_OBJECT_SETREF (ares, async_callback, (MonoObject *)async_callback);
MONO_OBJECT_SETREF (msg, async_result, ares);
msg->call_type = CallType_BeginInvoke;
exc = NULL;
mono_remoting_invoke ((MonoObject *)tp->rp, msg, &exc, &out_args, error);
if (!mono_error_ok (error)) {
mono_error_set_pending_exception (error);
return NULL;
}
if (exc)
mono_set_pending_exception ((MonoException *) exc);
return ares;
}
}
#endif
klass = delegate->object.vtable->klass;
ERROR_DECL (begin_invoke_error);
method = mono_get_delegate_begin_invoke_checked (klass, begin_invoke_error);
mono_error_cleanup (begin_invoke_error); /* if we can't call BeginInvoke, fall back on Invoke */
if (!method)
method = mono_get_delegate_invoke (klass);
g_assert (method);
MonoAsyncResult *result = mono_threadpool_begin_invoke (mono_domain_get (), (MonoObject*) delegate, method, params, error);
mono_error_set_pending_exception (error);
return result;
}
static char*
mono_signature_to_name (MonoMethodSignature *sig, const char *prefix)
{
int i;
char *result;
GString *res = g_string_new ("");
if (prefix) {
g_string_append (res, prefix);
g_string_append_c (res, '_');
}
mono_type_get_desc (res, sig->ret, FALSE);
if (sig->hasthis)
g_string_append (res, "__this__");
for (i = 0; i < sig->param_count; ++i) {
g_string_append_c (res, '_');
mono_type_get_desc (res, sig->params [i], FALSE);
}
result = res->str;
g_string_free (res, FALSE);
return result;
}
/**
* mono_marshal_get_string_encoding:
*
* Return the string encoding which should be used for a given parameter.
*/
MonoMarshalNative
mono_marshal_get_string_encoding (MonoMethodPInvoke *piinfo, MonoMarshalSpec *spec)
{
/* First try the parameter marshal info */
if (spec) {
if (spec->native == MONO_NATIVE_LPARRAY) {
if ((spec->data.array_data.elem_type != 0) && (spec->data.array_data.elem_type != MONO_NATIVE_MAX))
return spec->data.array_data.elem_type;
}
else
return spec->native;
}
if (!piinfo)
return MONO_NATIVE_LPSTR;
/* Then try the method level marshal info */
switch (piinfo->piflags & PINVOKE_ATTRIBUTE_CHAR_SET_MASK) {
case PINVOKE_ATTRIBUTE_CHAR_SET_ANSI:
return MONO_NATIVE_LPSTR;
case PINVOKE_ATTRIBUTE_CHAR_SET_UNICODE:
return MONO_NATIVE_LPWSTR;
case PINVOKE_ATTRIBUTE_CHAR_SET_AUTO:
#ifdef TARGET_WIN32
return MONO_NATIVE_LPWSTR;
#else
return MONO_NATIVE_LPSTR;
#endif
default:
return MONO_NATIVE_LPSTR;
}
}
MonoMarshalConv
mono_marshal_get_string_to_ptr_conv (MonoMethodPInvoke *piinfo, MonoMarshalSpec *spec)
{
MonoMarshalNative encoding = mono_marshal_get_string_encoding (piinfo, spec);
switch (encoding) {
case MONO_NATIVE_LPWSTR:
return MONO_MARSHAL_CONV_STR_LPWSTR;
case MONO_NATIVE_LPSTR:
case MONO_NATIVE_VBBYREFSTR:
return MONO_MARSHAL_CONV_STR_LPSTR;
case MONO_NATIVE_LPTSTR:
return MONO_MARSHAL_CONV_STR_LPTSTR;
case MONO_NATIVE_BSTR:
return MONO_MARSHAL_CONV_STR_BSTR;
case MONO_NATIVE_UTF8STR:
return MONO_MARSHAL_CONV_STR_UTF8STR;
default:
return MONO_MARSHAL_CONV_INVALID;
}
}
MonoMarshalConv
mono_marshal_get_stringbuilder_to_ptr_conv (MonoMethodPInvoke *piinfo, MonoMarshalSpec *spec)
{
MonoMarshalNative encoding = mono_marshal_get_string_encoding (piinfo, spec);
switch (encoding) {
case MONO_NATIVE_LPWSTR:
return MONO_MARSHAL_CONV_SB_LPWSTR;
case MONO_NATIVE_LPSTR:
return MONO_MARSHAL_CONV_SB_LPSTR;
case MONO_NATIVE_UTF8STR:
return MONO_MARSHAL_CONV_SB_UTF8STR;
case MONO_NATIVE_LPTSTR:
return MONO_MARSHAL_CONV_SB_LPTSTR;
default:
return MONO_MARSHAL_CONV_INVALID;
}
}
MonoMarshalConv
mono_marshal_get_ptr_to_string_conv (MonoMethodPInvoke *piinfo, MonoMarshalSpec *spec, gboolean *need_free)
{
MonoMarshalNative encoding = mono_marshal_get_string_encoding (piinfo, spec);
*need_free = TRUE;
switch (encoding) {
case MONO_NATIVE_LPWSTR:
*need_free = FALSE;
return MONO_MARSHAL_CONV_LPWSTR_STR;
case MONO_NATIVE_UTF8STR:
return MONO_MARSHAL_CONV_UTF8STR_STR;
case MONO_NATIVE_LPSTR:
case MONO_NATIVE_VBBYREFSTR:
return MONO_MARSHAL_CONV_LPSTR_STR;
case MONO_NATIVE_LPTSTR:
#ifdef TARGET_WIN32
*need_free = FALSE;
#endif
return MONO_MARSHAL_CONV_LPTSTR_STR;
case MONO_NATIVE_BSTR:
return MONO_MARSHAL_CONV_BSTR_STR;
default:
return MONO_MARSHAL_CONV_INVALID;
}
}
MonoMarshalConv
mono_marshal_get_ptr_to_stringbuilder_conv (MonoMethodPInvoke *piinfo, MonoMarshalSpec *spec, gboolean *need_free)
{
MonoMarshalNative encoding = mono_marshal_get_string_encoding (piinfo, spec);
*need_free = TRUE;
switch (encoding) {
case MONO_NATIVE_LPWSTR:
/*
* mono_string_builder_to_utf16 does not allocate a
* new buffer, so no need to free it.
*/
*need_free = FALSE;
return MONO_MARSHAL_CONV_LPWSTR_SB;
case MONO_NATIVE_UTF8STR:
return MONO_MARSHAL_CONV_UTF8STR_SB;
case MONO_NATIVE_LPSTR:
return MONO_MARSHAL_CONV_LPSTR_SB;
break;
case MONO_NATIVE_LPTSTR:
return MONO_MARSHAL_CONV_LPTSTR_SB;
break;
default:
return MONO_MARSHAL_CONV_INVALID;
}
}
/*
* Return whenever a field of a native structure or an array member needs to
* be freed.
*/
gboolean
mono_marshal_need_free (MonoType *t, MonoMethodPInvoke *piinfo, MonoMarshalSpec *spec)
{
MonoMarshalNative encoding;
switch (t->type) {
case MONO_TYPE_VALUETYPE:
/* FIXME: Optimize this */
return TRUE;
case MONO_TYPE_OBJECT:
case MONO_TYPE_CLASS:
if (t->data.klass == mono_defaults.stringbuilder_class) {
gboolean need_free;
mono_marshal_get_ptr_to_stringbuilder_conv (piinfo, spec, &need_free);
return need_free;
}
return FALSE;
case MONO_TYPE_STRING:
encoding = mono_marshal_get_string_encoding (piinfo, spec);
return (encoding == MONO_NATIVE_LPWSTR) ? FALSE : TRUE;
default:
return FALSE;
}
}
/*
* Return the hash table pointed to by VAR, lazily creating it if neccesary.
*/
static GHashTable*
get_cache (GHashTable **var, GHashFunc hash_func, GCompareFunc equal_func)
{
if (!(*var)) {
mono_marshal_lock ();
if (!(*var)) {
GHashTable *cache =
g_hash_table_new (hash_func, equal_func);
mono_memory_barrier ();
*var = cache;
}
mono_marshal_unlock ();
}
return *var;
}
GHashTable*
mono_marshal_get_cache (GHashTable **var, GHashFunc hash_func, GCompareFunc equal_func)
{
return get_cache (var, hash_func, equal_func);
}
MonoMethod*
mono_marshal_find_in_cache (GHashTable *cache, gpointer key)
{
MonoMethod *res;
mono_marshal_lock ();
res = (MonoMethod *)g_hash_table_lookup (cache, key);
mono_marshal_unlock ();
return res;
}
/*
* mono_mb_create:
*
* Create a MonoMethod from MB, set INFO as wrapper info.
*/
MonoMethod*
mono_mb_create (MonoMethodBuilder *mb, MonoMethodSignature *sig,
int max_stack, WrapperInfo *info)
{
MonoMethod *res;
res = mono_mb_create_method (mb, sig, max_stack);
if (info)
mono_marshal_set_wrapper_info (res, info);
return res;
}
/* Create the method from the builder and place it in the cache */
MonoMethod*
mono_mb_create_and_cache_full (GHashTable *cache, gpointer key,
MonoMethodBuilder *mb, MonoMethodSignature *sig,
int max_stack, WrapperInfo *info, gboolean *out_found)
{
MonoMethod *res;
if (out_found)
*out_found = FALSE;
mono_marshal_lock ();
res = (MonoMethod *)g_hash_table_lookup (cache, key);
mono_marshal_unlock ();
if (!res) {
MonoMethod *newm;
newm = mono_mb_create_method (mb, sig, max_stack);
mono_marshal_lock ();
res = (MonoMethod *)g_hash_table_lookup (cache, key);
if (!res) {
res = newm;
g_hash_table_insert (cache, key, res);
mono_marshal_set_wrapper_info (res, info);
mono_marshal_unlock ();
} else {
if (out_found)
*out_found = TRUE;
mono_marshal_unlock ();
mono_free_method (newm);
}
}
return res;
}
MonoMethod*
mono_mb_create_and_cache (GHashTable *cache, gpointer key,
MonoMethodBuilder *mb, MonoMethodSignature *sig,
int max_stack)
{
return mono_mb_create_and_cache_full (cache, key, mb, sig, max_stack, NULL, NULL);
}
/**
* mono_marshal_method_from_wrapper:
*/
MonoMethod *
mono_marshal_method_from_wrapper (MonoMethod *wrapper)
{
MonoMethod *m;
int wrapper_type = wrapper->wrapper_type;
WrapperInfo *info;
if (wrapper_type == MONO_WRAPPER_NONE || wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
return wrapper;
info = mono_marshal_get_wrapper_info (wrapper);
switch (wrapper_type) {
case MONO_WRAPPER_REMOTING_INVOKE:
case MONO_WRAPPER_REMOTING_INVOKE_WITH_CHECK:
case MONO_WRAPPER_XDOMAIN_INVOKE:
m = info->d.remoting.method;
if (wrapper->is_inflated) {
ERROR_DECL (error);
MonoMethod *result;
/*
* A method cannot be inflated and a wrapper at the same time, so the wrapper info
* contains an uninflated method.
*/
result = mono_class_inflate_generic_method_checked (m, mono_method_get_context (wrapper), error);
g_assert (mono_error_ok (error)); /* FIXME don't swallow the error */
return result;
}
return m;
case MONO_WRAPPER_SYNCHRONIZED:
m = info->d.synchronized.method;
if (wrapper->is_inflated) {
ERROR_DECL (error);
MonoMethod *result;
result = mono_class_inflate_generic_method_checked (m, mono_method_get_context (wrapper), error);
g_assert (mono_error_ok (error)); /* FIXME don't swallow the error */
return result;
}
return m;
case MONO_WRAPPER_UNBOX:
return info->d.unbox.method;
case MONO_WRAPPER_MANAGED_TO_NATIVE:
if (info && (info->subtype == WRAPPER_SUBTYPE_NONE || info->subtype == WRAPPER_SUBTYPE_NATIVE_FUNC_AOT || info->subtype == WRAPPER_SUBTYPE_PINVOKE))
return info->d.managed_to_native.method;
else
return NULL;
case MONO_WRAPPER_RUNTIME_INVOKE:
if (info && (info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT || info->subtype == WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL))
return info->d.runtime_invoke.method;
else
return NULL;
case MONO_WRAPPER_DELEGATE_INVOKE:
if (info)
return info->d.delegate_invoke.method;
else
return NULL;
default:
return NULL;
}
}
/*
* mono_marshal_get_wrapper_info:
*
* Retrieve the WrapperInfo structure associated with WRAPPER.
*/
WrapperInfo*
mono_marshal_get_wrapper_info (MonoMethod *wrapper)
{
g_assert (wrapper->wrapper_type);
return (WrapperInfo *)mono_method_get_wrapper_data (wrapper, 1);
}
/*
* mono_marshal_set_wrapper_info:
*
* Set the WrapperInfo structure associated with the wrapper
* method METHOD to INFO.
*/
void
mono_marshal_set_wrapper_info (MonoMethod *method, WrapperInfo *info)
{
void **datav;
/* assert */
if (method->wrapper_type == MONO_WRAPPER_NONE || method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
return;
datav = (void **)((MonoMethodWrapper *)method)->method_data;
datav [1] = info;
}
WrapperInfo*
mono_wrapper_info_create (MonoMethodBuilder *mb, WrapperSubtype subtype)
{
WrapperInfo *info;
info = (WrapperInfo *)mono_image_alloc0 (get_method_image (mb->method), sizeof (WrapperInfo));
info->subtype = subtype;
return info;
}
/*
* get_wrapper_target_class:
*
* Return the class where a wrapper method should be placed.
*/
static MonoClass*
get_wrapper_target_class (MonoImage *image)
{
ERROR_DECL (error);
MonoClass *klass;
/*
* Notes:
* - can't put all wrappers into an mscorlib class, because they reference
* metadata (signature) so they should be put into the same image as the
* method they wrap, so they are unloaded together.
* - putting them into a class with a type initalizer could cause the
* initializer to be executed which can be a problem if the wrappers are
* shared.
* - putting them into an inflated class can cause problems if the the
* class is deleted because it references an image which is unloaded.
* To avoid these problems, we put the wrappers into the <Module> class of
* the image.
*/
if (image_is_dynamic (image)) {
klass = ((MonoDynamicImage*)image)->wrappers_type;
} else {
klass = mono_class_get_checked (image, mono_metadata_make_token (MONO_TABLE_TYPEDEF, 1), error);
g_assert (mono_error_ok (error)); /* FIXME don't swallow the error */
}
g_assert (klass);
return klass;
}
/*
* Wrappers for generic methods should be instances of generic wrapper methods, i.e .the wrapper for Sort<int> should be
* an instance of the wrapper for Sort<T>. This is required for full-aot to work.
*/
/*
* check_generic_wrapper_cache:
*
* Check CACHE for the wrapper of the generic instance ORIG_METHOD, and return it if it is found.
* KEY should be the key for ORIG_METHOD in the cache, while DEF_KEY should be the key of its
* generic method definition.
*/
static MonoMethod*
check_generic_wrapper_cache (GHashTable *cache, MonoMethod *orig_method, gpointer key, gpointer def_key)
{
MonoMethod *res;
MonoMethod *inst, *def;
MonoGenericContext *ctx;
g_assert (orig_method->is_inflated);
ctx = mono_method_get_context (orig_method);
/*
* Look for the instance
*/
res = mono_marshal_find_in_cache (cache, key);
if (res)
return res;
/*
* Look for the definition
*/
def = mono_marshal_find_in_cache (cache, def_key);
if (def) {
ERROR_DECL (error);
inst = mono_class_inflate_generic_method_checked (def, ctx, error);
g_assert (mono_error_ok (error)); /* FIXME don't swallow the error */
/* Cache it */
mono_memory_barrier ();
mono_marshal_lock ();
res = (MonoMethod *)g_hash_table_lookup (cache, key);
if (!res) {
g_hash_table_insert (cache, key, inst);
res = inst;
}
mono_marshal_unlock ();
return res;
}
return NULL;
}
static MonoMethod*
cache_generic_wrapper (GHashTable *cache, MonoMethod *orig_method, MonoMethod *def, MonoGenericContext *ctx, gpointer key)
{
ERROR_DECL (error);
MonoMethod *inst, *res;
/*
* We use the same cache for the generic definition and the instances.
*/
inst = mono_class_inflate_generic_method_checked (def, ctx, error);
g_assert (mono_error_ok (error)); /* FIXME don't swallow the error */
mono_memory_barrier ();
mono_marshal_lock ();
res = (MonoMethod *)g_hash_table_lookup (cache, key);
if (!res) {
g_hash_table_insert (cache, key, inst);
res = inst;
}
mono_marshal_unlock ();
return res;
}
static MonoMethod*
check_generic_delegate_wrapper_cache (GHashTable *cache, MonoMethod *orig_method, MonoMethod *def_method, MonoGenericContext *ctx)
{
ERROR_DECL (error);
MonoMethod *res;
MonoMethod *inst, *def;
/*
* Look for the instance
*/
res = mono_marshal_find_in_cache (cache, orig_method->klass);
if (res)
return res;
/*
* Look for the definition
*/
def = mono_marshal_find_in_cache (cache, def_method->klass);
if (def) {
inst = mono_class_inflate_generic_method_checked (def, ctx, error);
g_assert (mono_error_ok (error)); /* FIXME don't swallow the error */
/* Cache it */
mono_memory_barrier ();
mono_marshal_lock ();
res = (MonoMethod *)g_hash_table_lookup (cache, orig_method->klass);
if (!res) {
g_hash_table_insert (cache, orig_method->klass, inst);
res = inst;
}
mono_marshal_unlock ();
return res;
}
return NULL;
}
static MonoMethod*
cache_generic_delegate_wrapper (GHashTable *cache, MonoMethod *orig_method, MonoMethod *def, MonoGenericContext *ctx)
{
ERROR_DECL (error);
MonoMethod *inst, *res;
WrapperInfo *ginfo, *info;
/*
* We use the same cache for the generic definition and the instances.
*/
inst = mono_class_inflate_generic_method_checked (def, ctx, error);
g_assert (mono_error_ok (error)); /* FIXME don't swallow the error */
ginfo = mono_marshal_get_wrapper_info (def);
if (ginfo) {
info = (WrapperInfo *)mono_image_alloc0 (m_class_get_image (def->klass), sizeof (WrapperInfo));
info->subtype = ginfo->subtype;
if (info->subtype == WRAPPER_SUBTYPE_NONE) {
info->d.delegate_invoke.method = mono_class_inflate_generic_method_checked (ginfo->d.delegate_invoke.method, ctx, error);
mono_error_assert_ok (error);
}
}
mono_memory_barrier ();
mono_marshal_lock ();
res = (MonoMethod *)g_hash_table_lookup (cache, orig_method->klass);
if (!res) {
g_hash_table_insert (cache, orig_method->klass, inst);
res = inst;
}
mono_marshal_unlock ();
return res;
}
static void
emit_delegate_begin_invoke_noilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig)
{
}
/**
* mono_marshal_get_delegate_begin_invoke:
*/
MonoMethod *
mono_marshal_get_delegate_begin_invoke (MonoMethod *method)
{
MonoMethodSignature *sig;
MonoMethodBuilder *mb;
MonoMethod *res;
GHashTable *cache;
char *name;
MonoGenericContext *ctx = NULL;
MonoMethod *orig_method = NULL;
g_assert (method && m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class &&
!strcmp (method->name, "BeginInvoke"));
/*
* For generic delegates, create a generic wrapper, and returns an instance to help AOT.
*/
if (method->is_inflated) {
orig_method = method;
ctx = &((MonoMethodInflated*)method)->context;
method = ((MonoMethodInflated*)method)->declaring;
}
sig = mono_signature_no_pinvoke (method);
/*
* Check cache
*/
if (ctx) {
cache = get_cache (&((MonoMethodInflated*)orig_method)->owner->wrapper_caches.delegate_begin_invoke_cache, mono_aligned_addr_hash, NULL);
res = check_generic_delegate_wrapper_cache (cache, orig_method, method, ctx);
if (res)
return res;
} else {
cache = get_cache (&get_method_image (method)->wrapper_caches.delegate_begin_invoke_cache,
(GHashFunc)mono_signature_hash,
(GCompareFunc)mono_metadata_signature_equal);
if ((res = mono_marshal_find_in_cache (cache, sig)))
return res;
}
g_assert (sig->hasthis);
name = mono_signature_to_name (sig, "begin_invoke");
if (ctx)
mb = mono_mb_new (method->klass, name, MONO_WRAPPER_DELEGATE_BEGIN_INVOKE);
else
mb = mono_mb_new (get_wrapper_target_class (get_method_image (method)), name, MONO_WRAPPER_DELEGATE_BEGIN_INVOKE);
g_free (name);
get_marshal_cb ()->emit_delegate_begin_invoke (mb, sig);
if (ctx) {
MonoMethod *def;
def = mono_mb_create_and_cache (cache, method->klass, mb, sig, sig->param_count + 16);
res = cache_generic_delegate_wrapper (cache, orig_method, def, ctx);
} else {
res = mono_mb_create_and_cache (cache, sig, mb, sig, sig->param_count + 16);
}
mono_mb_free (mb);
return res;
}
/* This is a JIT icall, it sets the pending exception and returns NULL on error. */
MonoObject *
mono_delegate_end_invoke (MonoDelegate *delegate, gpointer *params)
{
ERROR_DECL (error);
MonoDomain *domain = mono_domain_get ();
MonoAsyncResult *ares;
MonoMethod *method = NULL;
MonoMethodSignature *sig;
MonoMethodMessage *msg;
MonoObject *res, *exc;
MonoArray *out_args;
MonoClass *klass;
g_assert (delegate);
if (!delegate->method_info) {
g_assert (delegate->method);
MonoReflectionMethod *rm = mono_method_get_object_checked (domain, delegate->method, NULL, error);
if (!mono_error_ok (error)) {
mono_error_set_pending_exception (error);
return NULL;
}
MONO_OBJECT_SETREF (delegate, method_info, rm);
}
if (!delegate->method_info || !delegate->method_info->method)
g_assert_not_reached ();
klass = delegate->object.vtable->klass;
method = mono_get_delegate_end_invoke_checked (klass, error);
mono_error_assert_ok (error);
g_assert (method != NULL);
sig = mono_signature_no_pinvoke (method);
msg = mono_method_call_message_new (method, params, NULL, NULL, NULL, error);
if (mono_error_set_pending_exception (error))
return NULL;
ares = (MonoAsyncResult *)mono_array_get (msg->args, gpointer, sig->param_count - 1);
if (ares == NULL) {
mono_error_set_remoting (error, "The async result object is null or of an unexpected type.");
mono_error_set_pending_exception (error);
return NULL;
}
if (ares->async_delegate != (MonoObject*)delegate) {
mono_error_set_invalid_operation (error,
"%s", "The IAsyncResult object provided does not match this delegate.");
mono_error_set_pending_exception (error);
return NULL;
}
#ifndef DISABLE_REMOTING
if (delegate->target && mono_object_is_transparent_proxy (delegate->target)) {
MonoTransparentProxy* tp = (MonoTransparentProxy *)delegate->target;
msg = (MonoMethodMessage *)mono_object_new_checked (domain, mono_defaults.mono_method_message_class, error);
if (!mono_error_ok (error)) {
mono_error_set_pending_exception (error);
return NULL;
}
mono_message_init (domain, msg, delegate->method_info, NULL, error);
if (mono_error_set_pending_exception (error))
return NULL;
msg->call_type = CallType_EndInvoke;
MONO_OBJECT_SETREF (msg, async_result, ares);
res = mono_remoting_invoke ((MonoObject *)tp->rp, msg, &exc, &out_args, error);
if (!mono_error_ok (error)) {
mono_error_set_pending_exception (error);
return NULL;
}
} else
#endif
{
res = mono_threadpool_end_invoke (ares, &out_args, &exc, error);
if (mono_error_set_pending_exception (error))
return NULL;
}
if (exc) {
if (((MonoException*)exc)->stack_trace) {
ERROR_DECL_VALUE (inner_error);
char *strace = mono_string_to_utf8_checked (((MonoException*)exc)->stack_trace, &inner_error);
if (is_ok (&inner_error)) {
char *tmp;
tmp = g_strdup_printf ("%s\nException Rethrown at:\n", strace);
g_free (strace);
MonoString *tmp_str = mono_string_new_checked (domain, tmp, &inner_error);
g_free (tmp);
if (is_ok (&inner_error))
MONO_OBJECT_SETREF (((MonoException*)exc), stack_trace, tmp_str);
};
if (!is_ok (&inner_error))
mono_error_cleanup (&inner_error); /* no stack trace, but at least throw the original exception */
}
mono_set_pending_exception ((MonoException*)exc);
}
mono_method_return_message_restore (method, params, out_args, error);
mono_error_set_pending_exception (error);
return res;
}
static void
emit_delegate_end_invoke_noilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig)
{
}
/**
* mono_marshal_get_delegate_end_invoke:
*/
MonoMethod *
mono_marshal_get_delegate_end_invoke (MonoMethod *method)
{
MonoMethodSignature *sig;
MonoMethodBuilder *mb;
MonoMethod *res;
GHashTable *cache;
char *name;
MonoGenericContext *ctx = NULL;
MonoMethod *orig_method = NULL;
g_assert (method && m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class &&
!strcmp (method->name, "EndInvoke"));
/*
* For generic delegates, create a generic wrapper, and returns an instance to help AOT.
*/
if (method->is_inflated) {
orig_method = method;
ctx = &((MonoMethodInflated*)method)->context;
method = ((MonoMethodInflated*)method)->declaring;
}
sig = mono_signature_no_pinvoke (method);
/*
* Check cache
*/
if (ctx) {
cache = get_cache (&((MonoMethodInflated*)orig_method)->owner->wrapper_caches.delegate_end_invoke_cache, mono_aligned_addr_hash, NULL);
res = check_generic_delegate_wrapper_cache (cache, orig_method, method, ctx);
if (res)
return res;
} else {
cache = get_cache (&get_method_image (method)->wrapper_caches.delegate_end_invoke_cache,
(GHashFunc)mono_signature_hash,
(GCompareFunc)mono_metadata_signature_equal);
if ((res = mono_marshal_find_in_cache (cache, sig)))
return res;
}
g_assert (sig->hasthis);
name = mono_signature_to_name (sig, "end_invoke");
if (ctx)
mb = mono_mb_new (method->klass, name, MONO_WRAPPER_DELEGATE_END_INVOKE);
else
mb = mono_mb_new (get_wrapper_target_class (get_method_image (method)), name, MONO_WRAPPER_DELEGATE_END_INVOKE);
g_free (name);
get_marshal_cb ()->emit_delegate_end_invoke (mb, sig);
if (ctx) {
MonoMethod *def;
def = mono_mb_create_and_cache (cache, method->klass, mb, sig, sig->param_count + 16);
res = cache_generic_delegate_wrapper (cache, orig_method, def, ctx);
} else {
res = mono_mb_create_and_cache (cache, sig,
mb, sig, sig->param_count + 16);
}
mono_mb_free (mb);
return res;
}
typedef struct
{
MonoMethodSignature *sig;
gpointer pointer;
} SignaturePointerPair;
static guint
signature_pointer_pair_hash (gconstpointer data)
{
SignaturePointerPair *pair = (SignaturePointerPair*)data;
return mono_signature_hash (pair->sig) ^ mono_aligned_addr_hash (pair->pointer);
}
static gboolean
signature_pointer_pair_equal (gconstpointer data1, gconstpointer data2)
{
SignaturePointerPair *pair1 = (SignaturePointerPair*) data1, *pair2 = (SignaturePointerPair*) data2;
return mono_metadata_signature_equal (pair1->sig, pair2->sig) && (pair1->pointer == pair2->pointer);
}
static gboolean
signature_pointer_pair_matches_pointer (gpointer key, gpointer value, gpointer user_data)
{
SignaturePointerPair *pair = (SignaturePointerPair*)key;
return pair->pointer == user_data;
}
static void
free_signature_pointer_pair (SignaturePointerPair *pair)
{
g_free (pair);
}
static void
mb_skip_visibility_noilgen (MonoMethodBuilder *mb)
{
}
static void
mb_set_dynamic_noilgen (MonoMethodBuilder *mb)
{
}
static void
mb_emit_exception_noilgen (MonoMethodBuilder *mb, const char *exc_nspace, const char *exc_name, const char *msg)
{
}
static void
emit_delegate_invoke_internal_noilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodSignature *invoke_sig, gboolean static_method_with_first_arg_bound, gboolean callvirt, gboolean closed_over_null, MonoMethod *method, MonoMethod *target_method, MonoClass *target_class, MonoGenericContext *ctx, MonoGenericContainer *container)
{
}
MonoMethod *
mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt, gboolean static_method_with_first_arg_bound, MonoMethod *target_method)
{
MonoMethodSignature *sig, *invoke_sig;
MonoMethodBuilder *mb;
MonoMethod *res;
GHashTable *cache;
gpointer cache_key = NULL;
SignaturePointerPair key = { NULL, NULL };
SignaturePointerPair *new_key;
char *name;
MonoClass *target_class = NULL;
gboolean closed_over_null = FALSE;
MonoGenericContext *ctx = NULL;
MonoGenericContainer *container = NULL;
MonoMethod *orig_method = method;
WrapperInfo *info;
WrapperSubtype subtype = WRAPPER_SUBTYPE_NONE;
gboolean found;
g_assert (method && m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class &&
!strcmp (method->name, "Invoke"));
invoke_sig = sig = mono_signature_no_pinvoke (method);
/*
* If the delegate target is null, and the target method is not static, a virtual
* call is made to that method with the first delegate argument as this. This is
* a non-documented .NET feature.
*/
if (callvirt) {
subtype = WRAPPER_SUBTYPE_DELEGATE_INVOKE_VIRTUAL;
if (target_method->is_inflated) {
ERROR_DECL (error);
MonoType *target_type;
g_assert (method->signature->hasthis);
target_type = mono_class_inflate_generic_type_checked (method->signature->params [0],
mono_method_get_context (method), error);
mono_error_assert_ok (error); /* FIXME don't swallow the error */
target_class = mono_class_from_mono_type (target_type);
} else {
target_class = target_method->klass;
}
closed_over_null = sig->param_count == mono_method_signature (target_method)->param_count;
}
if (static_method_with_first_arg_bound) {
subtype = WRAPPER_SUBTYPE_DELEGATE_INVOKE_BOUND;
g_assert (!callvirt);
invoke_sig = mono_method_signature (target_method);
}
/*
* For generic delegates, create a generic wrapper, and return an instance to help AOT.
*/
if (method->is_inflated && subtype == WRAPPER_SUBTYPE_NONE) {
ctx = &((MonoMethodInflated*)method)->context;
method = ((MonoMethodInflated*)method)->declaring;
container = mono_method_get_generic_container (method);
if (!container)
container = mono_class_try_get_generic_container (method->klass); //FIXME is this a case of a try?
g_assert (container);
invoke_sig = sig = mono_signature_no_pinvoke (method);
}
/*
* Check cache
*/
if (ctx) {
cache = get_cache (&((MonoMethodInflated*)orig_method)->owner->wrapper_caches.delegate_invoke_cache, mono_aligned_addr_hash, NULL);
res = check_generic_delegate_wrapper_cache (cache, orig_method, method, ctx);
if (res)
return res;
cache_key = method->klass;
} else if (static_method_with_first_arg_bound) {
cache = get_cache (&get_method_image (target_method)->delegate_bound_static_invoke_cache,
(GHashFunc)mono_signature_hash,
(GCompareFunc)mono_metadata_signature_equal);
/*
* The wrapper is based on sig+invoke_sig, but sig can be derived from invoke_sig.
*/
res = mono_marshal_find_in_cache (cache, invoke_sig);
if (res)
return res;
cache_key = invoke_sig;
} else if (callvirt) {
GHashTable **cache_ptr;
cache_ptr = &mono_method_get_wrapper_cache (method)->delegate_abstract_invoke_cache;
/* We need to cache the signature+method pair */
mono_marshal_lock ();
if (!*cache_ptr)
*cache_ptr = g_hash_table_new_full (signature_pointer_pair_hash, (GEqualFunc)signature_pointer_pair_equal, (GDestroyNotify)free_signature_pointer_pair, NULL);
cache = *cache_ptr;
key.sig = invoke_sig;
key.pointer = target_method;
res = (MonoMethod *)g_hash_table_lookup (cache, &key);
mono_marshal_unlock ();
if (res)
return res;
} else {
// Inflated methods should not be in this cache because it's not stored on the imageset.
g_assert (!method->is_inflated);
cache = get_cache (&get_method_image (method)->wrapper_caches.delegate_invoke_cache,
(GHashFunc)mono_signature_hash,
(GCompareFunc)mono_metadata_signature_equal);
res = mono_marshal_find_in_cache (cache, sig);
if (res)
return res;
cache_key = sig;
}
if (!static_method_with_first_arg_bound) {
invoke_sig = mono_metadata_signature_dup_full (get_method_image (method), sig);
invoke_sig->hasthis = 0;
}
if (static_method_with_first_arg_bound)
name = mono_signature_to_name (invoke_sig, "invoke_bound");
else if (closed_over_null)
name = mono_signature_to_name (invoke_sig, "invoke_closed_over_null");
else if (callvirt)
name = mono_signature_to_name (invoke_sig, "invoke_callvirt");
else
name = mono_signature_to_name (invoke_sig, "invoke");
if (ctx)
mb = mono_mb_new (method->klass, name, MONO_WRAPPER_DELEGATE_INVOKE);
else
mb = mono_mb_new (get_wrapper_target_class (get_method_image (method)), name, MONO_WRAPPER_DELEGATE_INVOKE);
g_free (name);
get_marshal_cb ()->emit_delegate_invoke_internal (mb, sig, invoke_sig, static_method_with_first_arg_bound, callvirt, closed_over_null, method, target_method, target_class, ctx, container);
get_marshal_cb ()->mb_skip_visibility (mb);
info = mono_wrapper_info_create (mb, subtype);
info->d.delegate_invoke.method = method;
if (ctx) {
MonoMethod *def;
def = mono_mb_create_and_cache_full (cache, cache_key, mb, sig, sig->param_count + 16, info, NULL);
res = cache_generic_delegate_wrapper (cache, orig_method, def, ctx);
} else if (callvirt) {
new_key = g_new0 (SignaturePointerPair, 1);
*new_key = key;
res = mono_mb_create_and_cache_full (cache, new_key, mb, sig, sig->param_count + 16, info, &found);
if (found)
g_free (new_key);
} else {
res = mono_mb_create_and_cache_full (cache, cache_key, mb, sig, sig->param_count + 16, info, NULL);
}
mono_mb_free (mb);
/* mono_method_print_code (res); */
return res;
}
/**
* mono_marshal_get_delegate_invoke:
* The returned method invokes all methods in a multicast delegate.
*/
MonoMethod *
mono_marshal_get_delegate_invoke (MonoMethod *method, MonoDelegate *del)
{
gboolean callvirt = FALSE;
gboolean static_method_with_first_arg_bound = FALSE;
MonoMethod *target_method = NULL;
MonoMethodSignature *sig;
sig = mono_signature_no_pinvoke (method);
if (del && !del->target && del->method && mono_method_signature (del->method)->hasthis) {
callvirt = TRUE;
target_method = del->method;
}
if (del && del->method && mono_method_signature (del->method)->param_count == sig->param_count + 1 && (del->method->flags & METHOD_ATTRIBUTE_STATIC)) {
static_method_with_first_arg_bound = TRUE;
target_method = del->method;
}
return mono_marshal_get_delegate_invoke_internal (method, callvirt, static_method_with_first_arg_bound, target_method);
}
typedef struct {
MonoMethodSignature *ctor_sig;
MonoMethodSignature *sig;
} CtorSigPair;
/* protected by the marshal lock, contains CtorSigPair pointers */
static GSList *strsig_list = NULL;
static MonoMethodSignature *
lookup_string_ctor_signature (MonoMethodSignature *sig)
{
MonoMethodSignature *callsig;
CtorSigPair *cs;
GSList *item;
mono_marshal_lock ();
callsig = NULL;
for (item = strsig_list; item; item = item->next) {
cs = (CtorSigPair *)item->data;
/* mono_metadata_signature_equal () is safe to call with the marshal lock
* because it is lock-free.
*/
if (mono_metadata_signature_equal (sig, cs->ctor_sig)) {
callsig = cs->sig;
break;
}
}
mono_marshal_unlock ();
return callsig;
}
static MonoMethodSignature *
add_string_ctor_signature (MonoMethod *method)
{
MonoMethodSignature *callsig;
CtorSigPair *cs;
callsig = mono_metadata_signature_dup_full (get_method_image (method), mono_method_signature (method));
callsig->ret = m_class_get_byval_arg (mono_defaults.string_class);
cs = g_new (CtorSigPair, 1);
cs->sig = callsig;
cs->ctor_sig = mono_method_signature (method);
mono_marshal_lock ();
strsig_list = g_slist_prepend (strsig_list, cs);
mono_marshal_unlock ();
return callsig;
}
/*
* mono_marshal_get_string_ctor_signature:
*
* Return the modified signature used by string ctors (they return the newly created
* string).
*/
MonoMethodSignature*
mono_marshal_get_string_ctor_signature (MonoMethod *method)
{
MonoMethodSignature *sig = lookup_string_ctor_signature (mono_method_signature (method));
if (!sig)
sig = add_string_ctor_signature (method);
return sig;
}
static MonoType*
get_runtime_invoke_type (MonoType *t, gboolean ret)
{
if (t->byref) {
if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t)))
return t;
/* The result needs loaded indirectly */
if (ret)
return t;
/* Can't share this with 'I' as that needs another indirection */
return m_class_get_this_arg (mono_defaults.int_class);
}
if (MONO_TYPE_IS_REFERENCE (t))
return mono_get_object_type ();
if (ret)
/* The result needs to be boxed */
return t;
handle_enum:
switch (t->type) {
/* Can't share these as the argument needs to be loaded using sign/zero extension */
/*
case MONO_TYPE_U1:
return m_class_get_byval_arg (mono_defaults.sbyte_class);
case MONO_TYPE_U2:
return m_class_get_byval_arg (mono_defaults.int16_class);
case MONO_TYPE_U4:
return mono_get_int32_type ();
*/
case MONO_TYPE_U8:
return m_class_get_byval_arg (mono_defaults.int64_class);
case MONO_TYPE_BOOLEAN:
return m_class_get_byval_arg (mono_defaults.byte_class);
case MONO_TYPE_CHAR:
return m_class_get_byval_arg (mono_defaults.uint16_class);
case MONO_TYPE_U:
return mono_get_int_type ();
case MONO_TYPE_VALUETYPE:
if (m_class_is_enumtype (t->data.klass)) {
t = mono_class_enum_basetype (t->data.klass);
goto handle_enum;
}
return t;
default:
return t;
}
}
/*
* mono_marshal_get_runtime_invoke_sig:
*
* Return a common signature used for sharing runtime invoke wrappers.
*/
static MonoMethodSignature*
mono_marshal_get_runtime_invoke_sig (MonoMethodSignature *sig)
{
MonoMethodSignature *res = mono_metadata_signature_dup (sig);
int i;
res->generic_param_count = 0;
res->ret = get_runtime_invoke_type (sig->ret, TRUE);
for (i = 0; i < res->param_count; ++i)
res->params [i] = get_runtime_invoke_type (sig->params [i], FALSE);
return res;
}
static gboolean
runtime_invoke_signature_equal (MonoMethodSignature *sig1, MonoMethodSignature *sig2)
{
/* Can't share wrappers which return a vtype since it needs to be boxed */
if (sig1->ret != sig2->ret && !(MONO_TYPE_IS_REFERENCE (sig1->ret) && MONO_TYPE_IS_REFERENCE (sig2->ret)) && !mono_metadata_type_equal (sig1->ret, sig2->ret))
return FALSE;
else
return mono_metadata_signature_equal (sig1, sig2);
}
struct _MonoWrapperMethodCacheKey {
MonoMethod *method;
gboolean virtual_;
gboolean need_direct_wrapper;
};
struct _MonoWrapperSignatureCacheKey {
MonoMethodSignature *signature;
gboolean valuetype;
};
typedef struct _MonoWrapperMethodCacheKey MonoWrapperMethodCacheKey;
typedef struct _MonoWrapperSignatureCacheKey MonoWrapperSignatureCacheKey;
static guint
wrapper_cache_method_key_hash (MonoWrapperMethodCacheKey *key)
{
return mono_aligned_addr_hash (key->method) ^ (((!!key->virtual_) << 17) | ((!!key->need_direct_wrapper) << 19) * 17);
}
static guint
wrapper_cache_signature_key_hash (MonoWrapperSignatureCacheKey *key)
{
return mono_signature_hash (key->signature) ^ (((!!key->valuetype) << 18) * 17);
}
static gboolean
wrapper_cache_method_key_equal (MonoWrapperMethodCacheKey *key1, MonoWrapperMethodCacheKey *key2)
{
if (key1->virtual_ != key2->virtual_ || key1->need_direct_wrapper != key2->need_direct_wrapper)
return FALSE;
return key1->method == key2->method;
}
static gboolean
wrapper_cache_signature_key_equal (MonoWrapperSignatureCacheKey *key1, MonoWrapperSignatureCacheKey *key2)
{
if (key1->valuetype != key2->valuetype)
return FALSE;
return runtime_invoke_signature_equal (key1->signature, key2->signature);
}
static gboolean
wrapper_cache_method_matches_data (gpointer key, gpointer value, gpointer user_data)
{
MonoWrapperMethodCacheKey *mkey = (MonoWrapperMethodCacheKey *) key;
return mkey->method == (MonoMethod *) user_data;
}
/**
* mono_marshal_get_runtime_invoke:
* Generates IL code for the runtime invoke function:
*
* <code>MonoObject *runtime_invoke (MonoObject *this_obj, void **params, MonoObject **exc, void* method)</code>
*
* We also catch exceptions if \p exc is not NULL.
* If \p virtual is TRUE, then \p method is invoked virtually on \p this. This is useful since
* it means that the compiled code for \p method does not have to be looked up
* before calling the runtime invoke wrapper. In this case, the wrapper ignores
* its \p method argument.
*/
MonoMethod *
mono_marshal_get_runtime_invoke_full (MonoMethod *method, gboolean virtual_, gboolean need_direct_wrapper)
{
MonoMethodSignature *sig, *csig, *callsig;
MonoMethodBuilder *mb;
GHashTable *method_cache = NULL, *sig_cache;
GHashTable **cache_table = NULL;
MonoClass *target_klass;
MonoMethod *res = NULL;
static MonoMethodSignature *cctor_signature = NULL;
static MonoMethodSignature *finalize_signature = NULL;
char *name;
const char *param_names [16];
WrapperInfo *info;
MonoWrapperMethodCacheKey *method_key;
MonoWrapperMethodCacheKey method_key_lookup_only;
memset (&method_key_lookup_only, 0, sizeof (method_key_lookup_only));
method_key_lookup_only.method = method;
method_key_lookup_only.virtual_ = virtual_;
method_key_lookup_only.need_direct_wrapper = need_direct_wrapper;
method_key = &method_key_lookup_only;
g_assert (method);
if (!cctor_signature) {
cctor_signature = mono_metadata_signature_alloc (mono_defaults.corlib, 0);
cctor_signature->ret = mono_get_void_type ();
}
if (!finalize_signature) {
finalize_signature = mono_metadata_signature_alloc (mono_defaults.corlib, 0);
finalize_signature->ret = mono_get_void_type ();
finalize_signature->hasthis = 1;
}
cache_table = &mono_method_get_wrapper_cache (method)->runtime_invoke_method_cache;
method_cache = get_cache (cache_table, (GHashFunc) wrapper_cache_method_key_hash, (GCompareFunc) wrapper_cache_method_key_equal);
res = mono_marshal_find_in_cache (method_cache, method_key);
if (res)
return res;
if (method->string_ctor) {
callsig = lookup_string_ctor_signature (mono_method_signature (method));
if (!callsig)
callsig = add_string_ctor_signature (method);
} else {
if (method_is_dynamic (method))
callsig = mono_metadata_signature_dup_full (get_method_image (method), mono_method_signature (method));
else
callsig = mono_method_signature (method);
}
sig = mono_method_signature (method);
target_klass = get_wrapper_target_class (m_class_get_image (method->klass));
/* Try to share wrappers for non-corlib methods with simple signatures */
if (mono_metadata_signature_equal (callsig, cctor_signature)) {
callsig = cctor_signature;
target_klass = mono_defaults.object_class;
} else if (mono_metadata_signature_equal (callsig, finalize_signature)) {
callsig = finalize_signature;
target_klass = mono_defaults.object_class;
}
if (need_direct_wrapper || virtual_) {
/* Already searched at the start. We cannot cache those wrappers based
* on signatures because they contain a reference to the method */
} else {
MonoMethodSignature *tmp_sig;
callsig = mono_marshal_get_runtime_invoke_sig (callsig);
MonoWrapperSignatureCacheKey sig_key;
memset (&sig_key, 0, sizeof (sig_key));
sig_key.signature = callsig;
sig_key.valuetype = m_class_is_valuetype (method->klass);
cache_table = &mono_method_get_wrapper_cache (method)->runtime_invoke_signature_cache;
sig_cache = get_cache (cache_table, (GHashFunc) wrapper_cache_signature_key_hash, (GCompareFunc) wrapper_cache_signature_key_equal);
/* from mono_marshal_find_in_cache */
mono_marshal_lock ();
res = (MonoMethod *)g_hash_table_lookup (sig_cache, &sig_key);
mono_marshal_unlock ();
if (res) {
g_free (callsig);
return res;
}
/* Make a copy of the signature from the image mempool */
tmp_sig = callsig;
callsig = mono_metadata_signature_dup_full (m_class_get_image (target_klass), callsig);
g_free (tmp_sig);
}
csig = mono_metadata_signature_alloc (m_class_get_image (target_klass), 4);
MonoType *object_type = mono_get_object_type ();
MonoType *int_type = mono_get_int_type ();
csig->ret = object_type;
if (m_class_is_valuetype (method->klass) && mono_method_signature (method)->hasthis)
csig->params [0] = get_runtime_invoke_type (m_class_get_this_arg (method->klass), FALSE);
else
csig->params [0] = object_type;
csig->params [1] = int_type;
csig->params [2] = int_type;
csig->params [3] = int_type;
csig->pinvoke = 1;
#if TARGET_WIN32
/* This is called from runtime code so it has to be cdecl */
csig->call_convention = MONO_CALL_C;
#endif
name = mono_signature_to_name (callsig, virtual_ ? "runtime_invoke_virtual" : (need_direct_wrapper ? "runtime_invoke_direct" : "runtime_invoke"));
mb = mono_mb_new (target_klass, name, MONO_WRAPPER_RUNTIME_INVOKE);
g_free (name);
param_names [0] = "this";
param_names [1] = "params";
param_names [2] = "exc";
param_names [3] = "method";
get_marshal_cb ()->emit_runtime_invoke_body (mb, param_names, m_class_get_image (target_klass), method, sig, callsig, virtual_, need_direct_wrapper);
method_key = g_new (MonoWrapperMethodCacheKey, 1);
memcpy (method_key, &method_key_lookup_only, sizeof (MonoWrapperMethodCacheKey));
if (need_direct_wrapper || virtual_) {
get_marshal_cb ()->mb_skip_visibility (mb);
info = mono_wrapper_info_create (mb, virtual_ ? WRAPPER_SUBTYPE_RUNTIME_INVOKE_VIRTUAL : WRAPPER_SUBTYPE_RUNTIME_INVOKE_DIRECT);
info->d.runtime_invoke.method = method;
res = mono_mb_create_and_cache_full (method_cache, method_key, mb, csig, sig->param_count + 16, info, NULL);
} else {
MonoWrapperSignatureCacheKey *sig_key = g_new0 (MonoWrapperSignatureCacheKey, 1);
sig_key->signature = callsig;
sig_key->valuetype = m_class_is_valuetype (method->klass);
/* taken from mono_mb_create_and_cache */
mono_marshal_lock ();
res = (MonoMethod *)g_hash_table_lookup (sig_cache, sig_key);
mono_marshal_unlock ();
info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL);
info->d.runtime_invoke.sig = callsig;
/* Somebody may have created it before us */
if (!res) {
MonoMethod *newm;
newm = mono_mb_create (mb, csig, sig->param_count + 16, info);
mono_marshal_lock ();
res = (MonoMethod *)g_hash_table_lookup (sig_cache, sig_key);
if (!res) {
res = newm;
g_hash_table_insert (sig_cache, sig_key, res);
g_hash_table_insert (method_cache, method_key, res);
} else {
mono_free_method (newm);
g_free (sig_key);
g_free (method_key);
}
mono_marshal_unlock ();
} else {
g_free (sig_key);
g_free (method_key);
}
/* end mono_mb_create_and_cache */
}
mono_mb_free (mb);
return res;
}
MonoMethod *
mono_marshal_get_runtime_invoke (MonoMethod *method, gboolean virtual_)
{
gboolean need_direct_wrapper = FALSE;
if (virtual_)
need_direct_wrapper = TRUE;
if (method->dynamic)
need_direct_wrapper = TRUE;
if (m_class_get_rank (method->klass) && (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) &&
(method->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE)) {
/*
* Array Get/Set/Address methods. The JIT implements them using inline code
* so we need to create an invoke wrapper which calls the method directly.
*/
need_direct_wrapper = TRUE;
}
if (method->string_ctor) {
/* Can't share this as we push a string as this */
need_direct_wrapper = TRUE;
}
return mono_marshal_get_runtime_invoke_full (method, virtual_, need_direct_wrapper);
}
static void
emit_runtime_invoke_body_noilgen (MonoMethodBuilder *mb, const char **param_names, MonoImage *image, MonoMethod *method,
MonoMethodSignature *sig, MonoMethodSignature *callsig,
gboolean virtual_, gboolean need_direct_wrapper)
{
}
static void
emit_runtime_invoke_dynamic_noilgen (MonoMethodBuilder *mb)
{
}
/*
* mono_marshal_get_runtime_invoke_dynamic:
*
* Return a method which can be used to invoke managed methods from native code
* dynamically.
* The signature of the returned method is given by RuntimeInvokeDynamicFunction:
* void runtime_invoke (void *args, MonoObject **exc, void *compiled_method)
* ARGS should point to an architecture specific structure containing
* the arguments and space for the return value.
* The other arguments are the same as for runtime_invoke (), except that
* ARGS should contain the this argument too.
* This wrapper serves the same purpose as the runtime-invoke wrappers, but there
* is only one copy of it, which is useful in full-aot.
*/
MonoMethod*
mono_marshal_get_runtime_invoke_dynamic (void)
{
static MonoMethod *method;
MonoMethodSignature *csig;
MonoMethodBuilder *mb;
char *name;
WrapperInfo *info;
if (method)
return method;
csig = mono_metadata_signature_alloc (mono_defaults.corlib, 4);
MonoType *void_type = mono_get_void_type ();
MonoType *int_type = mono_get_int_type ();
csig->ret = void_type;
csig->params [0] = int_type;
csig->params [1] = int_type;
csig->params [2] = int_type;
csig->params [3] = int_type;
name = g_strdup ("runtime_invoke_dynamic");
mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_RUNTIME_INVOKE);
g_free (name);
get_marshal_cb ()->emit_runtime_invoke_dynamic (mb);
info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_RUNTIME_INVOKE_DYNAMIC);
mono_marshal_lock ();
/* double-checked locking */
if (!method)
method = mono_mb_create (mb, csig, 16, info);
mono_marshal_unlock ();
mono_mb_free (mb);
return method;
}
/*
* mono_marshal_get_runtime_invoke_for_sig:
*
* Return a runtime invoke wrapper for a given signature.
*/
MonoMethod *
mono_marshal_get_runtime_invoke_for_sig (MonoMethodSignature *sig)
{
MonoMethodSignature *csig, *callsig;
MonoMethodBuilder *mb;
MonoImage *image;
GHashTable *cache = NULL;
GHashTable **cache_table = NULL;
MonoMethod *res = NULL;
char *name;
const char *param_names [16];
WrapperInfo *info;
/* A simplified version of mono_marshal_get_runtime_invoke */
image = mono_defaults.corlib;
callsig = mono_marshal_get_runtime_invoke_sig (sig);
cache_table = &image->wrapper_caches.runtime_invoke_sig_cache;
cache = get_cache (cache_table, (GHashFunc)mono_signature_hash,
(GCompareFunc)runtime_invoke_signature_equal);
/* from mono_marshal_find_in_cache */
mono_marshal_lock ();
res = (MonoMethod *)g_hash_table_lookup (cache, callsig);
mono_marshal_unlock ();
if (res) {
g_free (callsig);
return res;
}
/* Make a copy of the signature from the image mempool */
callsig = mono_metadata_signature_dup_full (image, callsig);
MonoType *object_type = mono_get_object_type ();
MonoType *int_type = mono_get_int_type ();
csig = mono_metadata_signature_alloc (image, 4);
csig->ret = object_type;
csig->params [0] = object_type;
csig->params [1] = int_type;
csig->params [2] = int_type;
csig->params [3] = int_type;
csig->pinvoke = 1;
#if TARGET_WIN32
/* This is called from runtime code so it has to be cdecl */
csig->call_convention = MONO_CALL_C;
#endif
name = mono_signature_to_name (callsig, "runtime_invoke_sig");
mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_RUNTIME_INVOKE);
g_free (name);
param_names [0] = "this";
param_names [1] = "params";
param_names [2] = "exc";
param_names [3] = "method";
get_marshal_cb ()->emit_runtime_invoke_body (mb, param_names, image, NULL, sig, callsig, FALSE, FALSE);
/* taken from mono_mb_create_and_cache */
mono_marshal_lock ();
res = (MonoMethod *)g_hash_table_lookup (cache, callsig);
mono_marshal_unlock ();
info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_RUNTIME_INVOKE_NORMAL);
info->d.runtime_invoke.sig = callsig;
/* Somebody may have created it before us */
if (!res) {
MonoMethod *newm;
newm = mono_mb_create (mb, csig, sig->param_count + 16, info);
mono_marshal_lock ();
res = (MonoMethod *)g_hash_table_lookup (cache, callsig);
if (!res) {
res = newm;
g_hash_table_insert (cache, callsig, res);
} else {
mono_free_method (newm);
}
mono_marshal_unlock ();
}
/* end mono_mb_create_and_cache */
mono_mb_free (mb);
return res;
}
static void
emit_icall_wrapper_noilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig, gconstpointer func, MonoMethodSignature *csig2, gboolean check_exceptions)
{
}
/**
* mono_marshal_get_icall_wrapper:
* Generates IL code for the icall wrapper. The generated method
* calls the unmanaged code in \p func.
*/
MonoMethod *
mono_marshal_get_icall_wrapper (MonoMethodSignature *sig, const char *name, gconstpointer func, gboolean check_exceptions)
{
MonoMethodSignature *csig, *csig2;
MonoMethodBuilder *mb;
MonoMethod *res;
WrapperInfo *info;
GHashTable *cache = get_cache (& m_class_get_image (mono_defaults.object_class)->icall_wrapper_cache, mono_aligned_addr_hash, NULL);
if ((res = mono_marshal_find_in_cache (cache, (gpointer) func)))
return res;
g_assert (sig->pinvoke);
mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_MANAGED_TO_NATIVE);
mb->method->save_lmf = 1;
/* Add an explicit this argument */
if (sig->hasthis)
csig2 = mono_metadata_signature_dup_add_this (mono_defaults.corlib, sig, mono_defaults.object_class);
else
csig2 = mono_metadata_signature_dup_full (mono_defaults.corlib, sig);
get_marshal_cb ()->emit_icall_wrapper (mb, sig, func, csig2, check_exceptions);
csig = mono_metadata_signature_dup_full (mono_defaults.corlib, sig);
csig->pinvoke = 0;
if (csig->call_convention == MONO_CALL_VARARG)
csig->call_convention = 0;
info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_ICALL_WRAPPER);
info->d.icall.func = (gpointer)func;
res = mono_mb_create_and_cache_full (cache, (gpointer) func, mb, csig, csig->param_count + 16, info, NULL);
mono_mb_free (mb);
return res;
}
static int
emit_marshal_custom_noilgen (EmitMarshalContext *m, int argnum, MonoType *t,
MonoMarshalSpec *spec,
int conv_arg, MonoType **conv_arg_type,
MarshalAction action)
{
MonoType *int_type = mono_get_int_type ();
if (action == MARSHAL_ACTION_CONV_IN && t->type == MONO_TYPE_VALUETYPE)
*conv_arg_type = int_type;
return conv_arg;
}
static int
emit_marshal_asany_noilgen (EmitMarshalContext *m, int argnum, MonoType *t,
MonoMarshalSpec *spec,
int conv_arg, MonoType **conv_arg_type,
MarshalAction action)
{
return conv_arg;
}
static int
emit_marshal_vtype_noilgen (EmitMarshalContext *m, int argnum, MonoType *t,
MonoMarshalSpec *spec,
int conv_arg, MonoType **conv_arg_type,
MarshalAction action)
{
return conv_arg;
}
static int
emit_marshal_string_noilgen (EmitMarshalContext *m, int argnum, MonoType *t,
MonoMarshalSpec *spec,
int conv_arg, MonoType **conv_arg_type,
MarshalAction action)
{
MonoType *int_type = mono_get_int_type ();
switch (action) {
case MARSHAL_ACTION_CONV_IN:
*conv_arg_type = int_type;
break;
case MARSHAL_ACTION_MANAGED_CONV_IN:
*conv_arg_type = int_type;
break;
}
return conv_arg;
}
static int
emit_marshal_safehandle_noilgen (EmitMarshalContext *m, int argnum, MonoType *t,
MonoMarshalSpec *spec, int conv_arg,
MonoType **conv_arg_type, MarshalAction action)
{
MonoType *int_type = mono_get_int_type ();
if (action == MARSHAL_ACTION_CONV_IN)
*conv_arg_type = int_type;
return conv_arg;
}
static int
emit_marshal_handleref_noilgen (EmitMarshalContext *m, int argnum, MonoType *t,
MonoMarshalSpec *spec, int conv_arg,
MonoType **conv_arg_type, MarshalAction action)
{
MonoType *int_type = mono_get_int_type ();
if (action == MARSHAL_ACTION_CONV_IN)
*conv_arg_type = int_type;
return conv_arg;
}
static int
emit_marshal_object_noilgen (EmitMarshalContext *m, int argnum, MonoType *t,
MonoMarshalSpec *spec,
int conv_arg, MonoType **conv_arg_type,
MarshalAction action)
{
MonoType *int_type = mono_get_int_type ();
if (action == MARSHAL_ACTION_CONV_IN)
*conv_arg_type = int_type;
return conv_arg;
}
static int
emit_marshal_variant_noilgen (EmitMarshalContext *m, int argnum, MonoType *t,