-/**
- * \file
- * Copyright 2018 Microsoft
- * 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 "metadata/method-builder-ilgen.h"
-#include "metadata/method-builder-ilgen-internals.h"
-#include <mono/metadata/object.h>
-#include <mono/metadata/loader.h>
-#include "cil-coff.h"
+#include "mono/metadata/debug-helpers.h"
#include "metadata/marshal.h"
-#include "metadata/marshal-internals.h"
#include "metadata/marshal-ilgen.h"
+#include "metadata/marshal-lightweight.h"
#include "metadata/marshal-shared.h"
-#include "metadata/tabledefs.h"
-#include <mono/metadata/exception.h>
-#include <mono/metadata/appdomain.h>
-#include "mono/metadata/abi-details.h"
-#include "mono/metadata/class-abi-details.h"
-#include "mono/metadata/class-init.h"
-#include "mono/metadata/debug-helpers.h"
-#include "mono/metadata/threads.h"
-#include "mono/metadata/monitor.h"
+#include "metadata/method-builder-ilgen.h"
+#include "metadata/custom-attrs-internals.h"
+#include "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/reflection-internals.h"
+#include "metadata/reflection-internals.h"
#include "mono/metadata/handle.h"
-#include "mono/metadata/custom-attrs-internals.h"
-#include "mono/metadata/icall-internals.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>
-#include "icall-decl.h"
+
+
#define OPDEF(a,b,c,d,e,f,g,h,i,j) \
a = i,
static GENERATE_GET_CLASS_WITH_CACHE (date_time, "System", "DateTime");
static GENERATE_TRY_GET_CLASS_WITH_CACHE (icustom_marshaler, "System.Runtime.InteropServices", "ICustomMarshaler");
-static MonoImage*
-get_method_image (MonoMethod *method)
-{
- return m_class_get_image (method->klass);
-}
-
-/**
- * mono_mb_strdup:
- * \param mb the MethodBuilder
- * \param s a string
- *
- * Creates a copy of the string \p s that can be referenced from the IL of \c mb.
- *
- * \returns a pointer to the new string which is owned by the method builder
- */
-char*
-mono_mb_strdup (MonoMethodBuilder *mb, const char *s)
-{
- char *res;
- if (!mb->dynamic)
- res = mono_image_strdup (get_method_image (mb->method), s);
- else
- res = g_strdup (s);
- return res;
-}
-
-#ifndef DISABLE_COM
-
-// FIXME There are multiple caches of "Clear".
-G_GNUC_UNUSED
-static MonoMethod*
-mono_get_Variant_Clear (void)
-{
- MONO_STATIC_POINTER_INIT (MonoMethod, variant_clear)
- variant_clear = mono_marshal_shared_get_method_nofail (mono_class_get_variant_class (), "Clear", 0, 0);
- MONO_STATIC_POINTER_INIT_END (MonoMethod, variant_clear)
+static void emit_string_free_icall (MonoMethodBuilder *mb, MonoMarshalConv conv);
- g_assert (variant_clear);
- return variant_clear;
-}
+// TODO: Does this need to loose the mono_ prefix?
+static void mono_marshal_ilgen_legacy_init (void);
-#endif
+static gboolean ilgen_cb_inited = FALSE;
+static MonoMarshalIlgenCallbacks ilgen_marshal_cb;
-// FIXME There are multiple caches of "GetObjectForNativeVariant".
-G_GNUC_UNUSED
-static MonoMethod*
-mono_get_Marshal_GetObjectForNativeVariant (void)
+void
+mono_install_marshal_callbacks_ilgen (MonoMarshalIlgenCallbacks *cb)
{
- MONO_STATIC_POINTER_INIT (MonoMethod, get_object_for_native_variant)
- get_object_for_native_variant = mono_marshal_shared_get_method_nofail (mono_defaults.marshal_class, "GetObjectForNativeVariant", 1, 0);
- MONO_STATIC_POINTER_INIT_END (MonoMethod, get_object_for_native_variant)
-
- g_assert (get_object_for_native_variant);
- return get_object_for_native_variant;
+ g_assert (!ilgen_cb_inited);
+ g_assert (cb->version == MONO_MARSHAL_CALLBACKS_VERSION);
+ memcpy (&ilgen_marshal_cb, cb, sizeof (MonoMarshalIlgenCallbacks));
+ ilgen_cb_inited = TRUE;
}
-// FIXME There are multiple caches of "GetNativeVariantForObject".
-G_GNUC_UNUSED
-static MonoMethod*
-mono_get_Marshal_GetNativeVariantForObject (void)
-{
- MONO_STATIC_POINTER_INIT (MonoMethod, get_native_variant_for_object)
- get_native_variant_for_object = mono_marshal_shared_get_method_nofail (mono_defaults.marshal_class, "GetNativeVariantForObject", 2, 0);
- MONO_STATIC_POINTER_INIT_END (MonoMethod, get_native_variant_for_object)
-
- g_assert (get_native_variant_for_object);
- return get_native_variant_for_object;
-}
static void
emit_struct_free (MonoMethodBuilder *mb, MonoClass *klass, int struct_var)
mono_mb_emit_icall (mb, mono_struct_delete_old);
}
-static void
-emit_thread_interrupt_checkpoint (MonoMethodBuilder *mb)
+static int
+emit_marshal_array_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
+ MonoMarshalSpec *spec,
+ int conv_arg, MonoType **conv_arg_type,
+ MarshalAction action)
{
- // FIXME Put a boolean in MonoMethodBuilder instead.
- if (strstr (mb->name, "mono_thread_interruption_checkpoint"))
- return;
+ MonoMethodBuilder *mb = m->mb;
+ MonoClass *klass = mono_class_from_mono_type_internal (t);
+ MonoMarshalNative encoding;
- mono_marshal_shared_emit_thread_interrupt_checkpoint_call (mb, MONO_JIT_ICALL_mono_thread_interruption_checkpoint);
-}
+ encoding = mono_marshal_get_string_encoding (m->piinfo, spec);
+ MonoType *int_type = mono_get_int_type ();
+ MonoType *object_type = mono_get_object_type ();
-static void
-emit_thread_force_interrupt_checkpoint (MonoMethodBuilder *mb)
-{
- mono_marshal_shared_emit_thread_interrupt_checkpoint_call (mb, MONO_JIT_ICALL_mono_thread_force_interruption_checkpoint_noraise);
-}
+ MonoClass *eklass = m_class_get_element_class (klass);
-void
-mono_marshal_emit_thread_interrupt_checkpoint (MonoMethodBuilder *mb)
-{
- emit_thread_interrupt_checkpoint (mb);
-}
+ switch (action) {
+ case MARSHAL_ACTION_CONV_IN:
+ *conv_arg_type = object_type;
+ conv_arg = mono_mb_add_local (mb, object_type);
-void
-mono_marshal_emit_thread_force_interrupt_checkpoint (MonoMethodBuilder *mb)
-{
- emit_thread_force_interrupt_checkpoint (mb);
-}
+ if (m_class_is_blittable (eklass)) {
+ mono_mb_emit_ldarg (mb, argnum);
+ if (m_type_is_byref (t))
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (MONO_MARSHAL_CONV_ARRAY_LPARRAY, NULL));
+ mono_mb_emit_stloc (mb, conv_arg);
+ } else {
+#ifdef DISABLE_NONBLITTABLE
+ char *msg = g_strdup ("Non-blittable marshalling conversion is disabled");
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+#else
+ guint32 label1, label2, label3;
+ int index_var, src_var, dest_ptr, esize;
+ MonoMarshalConv conv;
+ gboolean is_string = FALSE;
-int
-mono_mb_emit_save_args (MonoMethodBuilder *mb, MonoMethodSignature *sig, gboolean save_this)
-{
- int i, params_var, tmp_var;
+ dest_ptr = mono_mb_add_local (mb, int_type);
- MonoType *int_type = mono_get_int_type ();
- /* allocate local (pointer) *params[] */
- params_var = mono_mb_add_local (mb, int_type);
- /* allocate local (pointer) tmp */
- tmp_var = mono_mb_add_local (mb, int_type);
-
- /* alloate space on stack to store an array of pointers to the arguments */
- mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P * (sig->param_count + 1));
- mono_mb_emit_byte (mb, CEE_PREFIX1);
- mono_mb_emit_byte (mb, CEE_LOCALLOC);
- mono_mb_emit_stloc (mb, params_var);
-
- /* tmp = params */
- mono_mb_emit_ldloc (mb, params_var);
- mono_mb_emit_stloc (mb, tmp_var);
-
- if (save_this && sig->hasthis) {
- mono_mb_emit_ldloc (mb, tmp_var);
- mono_mb_emit_ldarg_addr (mb, 0);
- mono_mb_emit_byte (mb, CEE_STIND_I);
- /* tmp = tmp + sizeof (gpointer) */
- if (sig->param_count)
- mono_mb_emit_add_to_local (mb, tmp_var, TARGET_SIZEOF_VOID_P);
+ if (eklass == mono_defaults.string_class) {
+ is_string = TRUE;
+ conv = mono_marshal_get_string_to_ptr_conv (m->piinfo, spec);
+ }
+ else if (eklass == mono_class_try_get_stringbuilder_class ()) {
+ is_string = TRUE;
+ conv = mono_marshal_get_stringbuilder_to_ptr_conv (m->piinfo, spec);
+ }
+ else
+ conv = MONO_MARSHAL_CONV_INVALID;
- }
+ if (is_string && conv == MONO_MARSHAL_CONV_INVALID) {
+ char *msg = g_strdup_printf ("string/stringbuilder marshalling conversion %d not implemented", encoding);
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ break;
+ }
- for (i = 0; i < sig->param_count; i++) {
- mono_mb_emit_ldloc (mb, tmp_var);
- mono_mb_emit_ldarg_addr (mb, i + sig->hasthis);
- mono_mb_emit_byte (mb, CEE_STIND_I);
- /* tmp = tmp + sizeof (gpointer) */
- if (i < (sig->param_count - 1))
- mono_mb_emit_add_to_local (mb, tmp_var, TARGET_SIZEOF_VOID_P);
- }
+ src_var = mono_mb_add_local (mb, object_type);
+ mono_mb_emit_ldarg (mb, argnum);
+ if (m_type_is_byref (t))
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, src_var);
- return params_var;
-}
+ /* Check null */
+ mono_mb_emit_ldloc (mb, src_var);
+ mono_mb_emit_stloc (mb, conv_arg);
+ mono_mb_emit_ldloc (mb, src_var);
+ label1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+ if (is_string)
+ esize = TARGET_SIZEOF_VOID_P;
+ else if (eklass == mono_defaults.char_class) /*can't call mono_marshal_type_size since it causes all sorts of asserts*/
+ esize = mono_pinvoke_is_unicode (m->piinfo) ? 2 : 1;
+ else
+ esize = mono_class_native_size (eklass, NULL);
-void
-mono_mb_emit_restore_result (MonoMethodBuilder *mb, MonoType *return_type)
-{
- MonoType *t = mono_type_get_underlying_type (return_type);
- MonoType *int_type = mono_get_int_type ();
+ /* allocate space for the native struct and store the address */
+ mono_mb_emit_icon (mb, esize);
+ mono_mb_emit_ldloc (mb, src_var);
+ mono_mb_emit_byte (mb, CEE_LDLEN);
- if (m_type_is_byref (return_type))
- return_type = int_type;
+ if (eklass == mono_defaults.string_class) {
+ /* Make the array bigger for the terminating null */
+ mono_mb_emit_byte (mb, CEE_LDC_I4_1);
+ mono_mb_emit_byte (mb, CEE_ADD);
+ }
+ mono_mb_emit_byte (mb, CEE_MUL);
+ mono_mb_emit_byte (mb, CEE_PREFIX1);
+ mono_mb_emit_byte (mb, CEE_LOCALLOC);
+ mono_mb_emit_stloc (mb, conv_arg);
- switch (t->type) {
- case MONO_TYPE_VOID:
- g_assert_not_reached ();
- break;
- case MONO_TYPE_PTR:
- case MONO_TYPE_FNPTR:
- case MONO_TYPE_STRING:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_OBJECT:
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY:
- /* nothing to do */
- break;
- 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:
- mono_mb_emit_op (mb, CEE_UNBOX, mono_class_from_mono_type_internal (return_type));
- mono_mb_emit_byte (mb, mono_type_to_ldind (return_type));
- break;
- case MONO_TYPE_GENERICINST:
- if (!mono_type_generic_inst_is_valuetype (t))
- break;
- /* fall through */
- case MONO_TYPE_VALUETYPE: {
- MonoClass *klass = mono_class_from_mono_type_internal (return_type);
- mono_mb_emit_op (mb, CEE_UNBOX, klass);
- mono_mb_emit_op (mb, CEE_LDOBJ, klass);
- break;
- }
- case MONO_TYPE_VAR:
- case MONO_TYPE_MVAR: {
- MonoClass *klass = mono_class_from_mono_type_internal (return_type);
- mono_mb_emit_op (mb, CEE_UNBOX_ANY, klass);
- break;
- }
- default:
- g_warning ("type 0x%x not handled", return_type->type);
- g_assert_not_reached ();
- }
+ mono_mb_emit_ldloc (mb, conv_arg);
+ mono_mb_emit_stloc (mb, dest_ptr);
- mono_mb_emit_byte (mb, CEE_RET);
-}
+ /* Emit marshalling loop */
+ index_var = mono_mb_add_local (mb, int_type);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_0);
+ mono_mb_emit_stloc (mb, index_var);
+ label2 = mono_mb_get_label (mb);
+ mono_mb_emit_ldloc (mb, index_var);
+ mono_mb_emit_ldloc (mb, src_var);
+ mono_mb_emit_byte (mb, CEE_LDLEN);
+ label3 = mono_mb_emit_branch (mb, CEE_BGE);
-/*
- * emit_invoke_call:
- *
- * Emit the call to the wrapper method from a runtime invoke wrapper.
- */
-static void
-emit_invoke_call (MonoMethodBuilder *mb, MonoMethod *method,
- MonoMethodSignature *sig, MonoMethodSignature *callsig,
- int loc_res,
- gboolean virtual_, gboolean need_direct_wrapper)
-{
- int i;
- int *tmp_nullable_locals;
- gboolean void_ret = FALSE;
- gboolean string_ctor = method && method->string_ctor;
-
- if (virtual_) {
- g_assert (sig->hasthis);
- g_assert (method->flags & METHOD_ATTRIBUTE_VIRTUAL);
- }
+ /* Emit marshalling code */
- if (sig->hasthis) {
- if (string_ctor) {
- /* This will call the code emitted by mono_marshal_get_native_wrapper () which ignores it */
- mono_mb_emit_icon (mb, 0);
- mono_mb_emit_byte (mb, CEE_CONV_I);
- } else {
- mono_mb_emit_ldarg (mb, 0);
- }
- }
+ if (is_string) {
+ int stind_op;
+ mono_mb_emit_ldloc (mb, dest_ptr);
+ mono_mb_emit_ldloc (mb, src_var);
+ mono_mb_emit_ldloc (mb, index_var);
+ mono_mb_emit_byte (mb, CEE_LDELEM_REF);
+ mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (conv, &stind_op));
+ mono_mb_emit_byte (mb, stind_op);
+ } else {
+ /* set the src_ptr */
+ mono_mb_emit_ldloc (mb, src_var);
+ mono_mb_emit_ldloc (mb, index_var);
+ mono_mb_emit_op (mb, CEE_LDELEMA, eklass);
+ mono_mb_emit_stloc (mb, 0);
- tmp_nullable_locals = g_new0 (int, sig->param_count);
+ /* set dst_ptr */
+ mono_mb_emit_ldloc (mb, dest_ptr);
+ mono_mb_emit_stloc (mb, 1);
- for (i = 0; i < sig->param_count; i++) {
- MonoType *t = sig->params [i];
- int type;
+ /* emit valuetype conversion code */
+ mono_marshal_shared_emit_struct_conv_full (mb, eklass, FALSE, 0, eklass == mono_defaults.char_class ? encoding : (MonoMarshalNative)-1);
+ }
- mono_mb_emit_ldarg (mb, 1);
- if (i) {
- mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P * i);
- mono_mb_emit_byte (mb, CEE_ADD);
- }
+ mono_mb_emit_add_to_local (mb, index_var, 1);
+ mono_mb_emit_add_to_local (mb, dest_ptr, esize);
- if (m_type_is_byref (t)) {
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- /* A Nullable<T> type don't have a boxed form, it's either null or a boxed T.
- * So to make this work we unbox it to a local variable and push a reference to that.
- */
- if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type_internal (t))) {
- tmp_nullable_locals [i] = mono_mb_add_local (mb, m_class_get_byval_arg (mono_class_from_mono_type_internal (t)));
+ mono_mb_emit_branch_label (mb, CEE_BR, label2);
- mono_mb_emit_op (mb, CEE_UNBOX_ANY, mono_class_from_mono_type_internal (t));
- mono_mb_emit_stloc (mb, tmp_nullable_locals [i]);
- mono_mb_emit_ldloc_addr (mb, tmp_nullable_locals [i]);
- }
- continue;
- }
+ mono_mb_patch_branch (mb, label3);
- type = sig->params [i]->type;
-handle_enum:
- switch (type) {
- case MONO_TYPE_I1:
- case MONO_TYPE_BOOLEAN:
- case MONO_TYPE_U1:
- case MONO_TYPE_I2:
- case MONO_TYPE_U2:
- case MONO_TYPE_CHAR:
- case MONO_TYPE_I:
- case MONO_TYPE_U:
- case MONO_TYPE_I4:
- case MONO_TYPE_U4:
- case MONO_TYPE_R4:
- case MONO_TYPE_R8:
- case MONO_TYPE_I8:
- case MONO_TYPE_U8:
- mono_mb_emit_no_nullcheck (mb);
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_no_nullcheck (mb);
- mono_mb_emit_byte (mb, mono_type_to_ldind (sig->params [i]));
- break;
- case MONO_TYPE_STRING:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_PTR:
- case MONO_TYPE_FNPTR:
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_OBJECT:
- mono_mb_emit_no_nullcheck (mb);
- mono_mb_emit_byte (mb, mono_type_to_ldind (sig->params [i]));
- break;
- case MONO_TYPE_GENERICINST:
- if (!mono_type_generic_inst_is_valuetype (sig->params [i])) {
- mono_mb_emit_no_nullcheck (mb);
- mono_mb_emit_byte (mb, mono_type_to_ldind (sig->params [i]));
- break;
+ if (eklass == mono_defaults.string_class) {
+ /* Null terminate */
+ mono_mb_emit_ldloc (mb, dest_ptr);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_0);
+ mono_mb_emit_byte (mb, CEE_STIND_I);
}
- t = m_class_get_byval_arg (t->data.generic_class->container_class);
- type = t->type;
- goto handle_enum;
- case MONO_TYPE_VALUETYPE:
- if (type == MONO_TYPE_VALUETYPE && m_class_is_enumtype (t->data.klass)) {
- type = mono_class_enum_basetype_internal (t->data.klass)->type;
- goto handle_enum;
- }
- mono_mb_emit_no_nullcheck (mb);
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- if (mono_class_is_nullable (mono_class_from_mono_type_internal (sig->params [i]))) {
- /* Need to convert a boxed vtype to an mp to a Nullable struct */
- mono_mb_emit_op (mb, CEE_UNBOX, mono_class_from_mono_type_internal (sig->params [i]));
- mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type_internal (sig->params [i]));
- } else {
- mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type_internal (sig->params [i]));
- }
- break;
- default:
- g_assert_not_reached ();
+ mono_mb_patch_branch (mb, label1);
+#endif
}
- }
- if (virtual_) {
- mono_mb_emit_op (mb, CEE_CALLVIRT, method);
- } else if (need_direct_wrapper) {
- mono_mb_emit_op (mb, CEE_CALL, method);
- } else {
- mono_mb_emit_ldarg (mb, 3);
- mono_mb_emit_calli (mb, callsig);
- }
+ break;
- if (m_type_is_byref (sig->ret)) {
- /* perform indirect load and return by value */
- int pos;
- mono_mb_emit_byte (mb, CEE_DUP);
- pos = mono_mb_emit_branch (mb, CEE_BRTRUE);
- mono_mb_emit_exception_full (mb, "Mono", "NullByRefReturnException", NULL);
- mono_mb_patch_branch (mb, pos);
+ case MARSHAL_ACTION_CONV_OUT: {
+#ifndef DISABLE_NONBLITTABLE
+ gboolean need_convert, need_free;
+ /* Unicode character arrays are implicitly marshalled as [Out] under MS.NET */
+ need_convert = ((eklass == mono_defaults.char_class) && (encoding == MONO_NATIVE_LPWSTR)) || (eklass == mono_class_try_get_stringbuilder_class ()) || (t->attrs & PARAM_ATTRIBUTE_OUT);
+ need_free = mono_marshal_need_free (m_class_get_byval_arg (eklass), m->piinfo, spec);
- int ldind_op;
- MonoType* ret_byval = m_class_get_byval_arg (mono_class_from_mono_type_internal (sig->ret));
- g_assert (!m_type_is_byref (ret_byval));
- // TODO: Handle null references
- ldind_op = mono_type_to_ldind (ret_byval);
- /* taken from similar code in mini-generic-sharing.c
- * we need to use mono_mb_emit_op to add method data when loading
- * a structure since method-to-ir needs this data for wrapper methods */
- if (ldind_op == CEE_LDOBJ)
- mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type_internal (ret_byval));
- else
- mono_mb_emit_byte (mb, ldind_op);
- }
+ if ((t->attrs & PARAM_ATTRIBUTE_OUT) && spec && spec->native == MONO_NATIVE_LPARRAY && spec->data.array_data.param_num != -1) {
+ int param_num = spec->data.array_data.param_num;
+ MonoType *param_type;
- switch (sig->ret->type) {
- case MONO_TYPE_VOID:
- if (!string_ctor)
- void_ret = TRUE;
- break;
- case MONO_TYPE_BOOLEAN:
- case MONO_TYPE_CHAR:
- case MONO_TYPE_I1:
- case MONO_TYPE_U1:
- case MONO_TYPE_I2:
- case MONO_TYPE_U2:
- case MONO_TYPE_I4:
- case MONO_TYPE_U4:
- case MONO_TYPE_I:
- case MONO_TYPE_U:
- case MONO_TYPE_R4:
- case MONO_TYPE_R8:
- case MONO_TYPE_I8:
- case MONO_TYPE_U8:
- case MONO_TYPE_VALUETYPE:
- case MONO_TYPE_TYPEDBYREF:
- case MONO_TYPE_GENERICINST:
- /* box value types */
- mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type_internal (sig->ret));
- break;
- case MONO_TYPE_STRING:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_OBJECT:
- /* nothing to do */
- break;
- case MONO_TYPE_PTR:
- /* The result is an IntPtr */
- mono_mb_emit_op (mb, CEE_BOX, mono_defaults.int_class);
- break;
- default:
- g_assert_not_reached ();
- }
-
- if (!void_ret)
- mono_mb_emit_stloc (mb, loc_res);
-
- /* Convert back nullable-byref arguments */
- for (i = 0; i < sig->param_count; i++) {
- MonoType *t = sig->params [i];
+ param_type = m->sig->params [param_num];
- /*
- * Box the result and put it back into the array, the caller will have
- * to obtain it from there.
- */
- if (m_type_is_byref (t) && t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type_internal (t))) {
- mono_mb_emit_ldarg (mb, 1);
- mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P * i);
- mono_mb_emit_byte (mb, CEE_ADD);
+ if (m_type_is_byref (param_type) && param_type->type != MONO_TYPE_I4) {
+ char *msg = g_strdup ("Not implemented.");
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ break;
+ }
- mono_mb_emit_ldloc (mb, tmp_nullable_locals [i]);
- mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type_internal (t));
+ if (m_type_is_byref (t) ) {
+ mono_mb_emit_ldarg (mb, argnum);
- mono_mb_emit_byte (mb, CEE_STIND_REF);
+ /* Create the managed array */
+ mono_mb_emit_ldarg (mb, param_num);
+ if (m_type_is_byref (m->sig->params [param_num]))
+ // FIXME: Support other types
+ mono_mb_emit_byte (mb, CEE_LDIND_I4);
+ mono_mb_emit_byte (mb, CEE_CONV_OVF_I);
+ mono_mb_emit_op (mb, CEE_NEWARR, eklass);
+ /* Store into argument */
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+ }
}
- }
-
- g_free (tmp_nullable_locals);
-}
-static void
-emit_runtime_invoke_body_ilgen (MonoMethodBuilder *mb, const char **param_names, MonoImage *image, MonoMethod *method,
- MonoMethodSignature *sig, MonoMethodSignature *callsig,
- gboolean virtual_, gboolean need_direct_wrapper)
-{
- gint32 labels [16];
- MonoExceptionClause *clause;
- int loc_res, loc_exc;
-
- mono_mb_set_param_names (mb, param_names);
-
- /* The wrapper looks like this:
- *
- * <interrupt check>
- * if (exc) {
- * try {
- * return <call>
- * } catch (Exception e) {
- * *exc = e;
- * }
- * } else {
- * return <call>
- * }
- */
+ if (need_convert || need_free) {
+ /* FIXME: Optimize blittable case */
+ guint32 label1, label2, label3;
+ int index_var, src_ptr, loc, esize;
- MonoType *object_type = mono_get_object_type ();
- /* allocate local 0 (object) tmp */
- loc_res = mono_mb_add_local (mb, object_type);
- /* allocate local 1 (object) exc */
- loc_exc = mono_mb_add_local (mb, object_type);
+ if ((eklass == mono_class_try_get_stringbuilder_class ()) || (eklass == mono_defaults.string_class))
+ esize = TARGET_SIZEOF_VOID_P;
+ else if (eklass == mono_defaults.char_class)
+ esize = mono_pinvoke_is_unicode (m->piinfo) ? 2 : 1;
+ else
+ esize = mono_class_native_size (eklass, NULL);
+ src_ptr = mono_mb_add_local (mb, int_type);
+ loc = mono_mb_add_local (mb, int_type);
- /* *exc is assumed to be initialized to NULL by the caller */
+ /* Check null */
+ mono_mb_emit_ldarg (mb, argnum);
+ if (m_type_is_byref (t))
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ label1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
- mono_mb_emit_byte (mb, CEE_LDARG_2);
- labels [0] = mono_mb_emit_branch (mb, CEE_BRFALSE);
+ mono_mb_emit_ldloc (mb, conv_arg);
+ mono_mb_emit_stloc (mb, src_ptr);
- /*
- * if (exc) case
- */
- labels [1] = mono_mb_get_label (mb);
- emit_thread_force_interrupt_checkpoint (mb);
- emit_invoke_call (mb, method, sig, callsig, loc_res, virtual_, need_direct_wrapper);
+ /* Emit marshalling loop */
+ index_var = mono_mb_add_local (mb, int_type);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_0);
+ mono_mb_emit_stloc (mb, index_var);
+ label2 = mono_mb_get_label (mb);
+ mono_mb_emit_ldloc (mb, index_var);
+ mono_mb_emit_ldarg (mb, argnum);
+ if (m_type_is_byref (t))
+ mono_mb_emit_byte (mb, CEE_LDIND_REF);
+ mono_mb_emit_byte (mb, CEE_LDLEN);
+ label3 = mono_mb_emit_branch (mb, CEE_BGE);
- labels [2] = mono_mb_emit_branch (mb, CEE_LEAVE);
+ /* Emit marshalling code */
- /* Add a try clause around the call */
- clause = (MonoExceptionClause *)mono_image_alloc0 (image, sizeof (MonoExceptionClause));
- clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
- clause->data.catch_class = mono_defaults.exception_class;
- clause->try_offset = labels [1];
- clause->try_len = mono_mb_get_label (mb) - labels [1];
+ if (eklass == mono_class_try_get_stringbuilder_class ()) {
+ gboolean need_free2;
+ MonoMarshalConv conv = mono_marshal_get_ptr_to_stringbuilder_conv (m->piinfo, spec, &need_free2);
- clause->handler_offset = mono_mb_get_label (mb);
+ g_assert (conv != MONO_MARSHAL_CONV_INVALID);
- /* handler code */
- mono_mb_emit_stloc (mb, loc_exc);
- mono_mb_emit_byte (mb, CEE_LDARG_2);
- mono_mb_emit_ldloc (mb, loc_exc);
- mono_mb_emit_byte (mb, CEE_STIND_REF);
+ /* dest */
+ mono_mb_emit_ldarg (mb, argnum);
+ if (m_type_is_byref (t))
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_ldloc (mb, index_var);
+ mono_mb_emit_byte (mb, CEE_LDELEM_REF);
- mono_mb_emit_branch (mb, CEE_LEAVE);
+ /* src */
+ mono_mb_emit_ldloc (mb, src_ptr);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
- clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
+ mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (conv, NULL));
- mono_mb_set_clauses (mb, 1, clause);
+ if (need_free) {
+ /* src */
+ mono_mb_emit_ldloc (mb, src_ptr);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_patch_branch (mb, labels [2]);
- mono_mb_emit_ldloc (mb, loc_res);
- mono_mb_emit_byte (mb, CEE_RET);
+ mono_mb_emit_icall (mb, mono_marshal_free);
+ }
+ }
+ else if (eklass == mono_defaults.string_class) {
+ if (need_free) {
+ /* src */
+ mono_mb_emit_ldloc (mb, src_ptr);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
- /*
- * if (!exc) case
- */
- mono_mb_patch_branch (mb, labels [0]);
- emit_thread_force_interrupt_checkpoint (mb);
- emit_invoke_call (mb, method, sig, callsig, loc_res, virtual_, need_direct_wrapper);
+ mono_mb_emit_icall (mb, mono_marshal_free);
+ }
+ }
+ else {
+ if (need_convert) {
+ /* set the src_ptr */
+ mono_mb_emit_ldloc (mb, src_ptr);
+ mono_mb_emit_stloc (mb, 0);
- mono_mb_emit_ldloc (mb, 0);
- mono_mb_emit_byte (mb, CEE_RET);
-}
+ /* set dst_ptr */
+ mono_mb_emit_ldarg (mb, argnum);
+ if (m_type_is_byref (t))
+ mono_mb_emit_byte (mb, CEE_LDIND_REF);
+ mono_mb_emit_ldloc (mb, index_var);
+ mono_mb_emit_op (mb, CEE_LDELEMA, eklass);
+ mono_mb_emit_stloc (mb, 1);
-static void
-emit_runtime_invoke_dynamic_ilgen (MonoMethodBuilder *mb)
-{
- int pos;
- MonoExceptionClause *clause;
+ /* emit valuetype conversion code */
+ mono_marshal_shared_emit_struct_conv_full (mb, eklass, TRUE, 0, eklass == mono_defaults.char_class ? encoding : (MonoMarshalNative)-1);
+ }
- MonoType *object_type = mono_get_object_type ();
- /* allocate local 0 (object) tmp */
- mono_mb_add_local (mb, object_type);
- /* allocate local 1 (object) exc */
- mono_mb_add_local (mb, object_type);
-
- /* cond set *exc to null */
- mono_mb_emit_byte (mb, CEE_LDARG_1);
- mono_mb_emit_byte (mb, CEE_BRFALSE_S);
- mono_mb_emit_byte (mb, 3);
- mono_mb_emit_byte (mb, CEE_LDARG_1);
- mono_mb_emit_byte (mb, CEE_LDNULL);
- mono_mb_emit_byte (mb, CEE_STIND_REF);
-
- emit_thread_force_interrupt_checkpoint (mb);
-
- mono_mb_emit_byte (mb, CEE_LDARG_0);
- mono_mb_emit_byte (mb, CEE_LDARG_2);
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_byte (mb, CEE_MONO_DYN_CALL);
+ if (need_free) {
+ mono_mb_emit_ldloc (mb, src_ptr);
+ mono_mb_emit_stloc (mb, loc);
- pos = mono_mb_emit_branch (mb, CEE_LEAVE);
+ emit_struct_free (mb, eklass, loc);
+ }
+ }
- clause = (MonoExceptionClause *)mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
- clause->flags = MONO_EXCEPTION_CLAUSE_FILTER;
- clause->try_len = mono_mb_get_label (mb);
+ mono_mb_emit_add_to_local (mb, index_var, 1);
+ mono_mb_emit_add_to_local (mb, src_ptr, esize);
- /* filter code */
- clause->data.filter_offset = mono_mb_get_label (mb);
+ mono_mb_emit_branch_label (mb, CEE_BR, label2);
- mono_mb_emit_byte (mb, CEE_POP);
- mono_mb_emit_byte (mb, CEE_LDARG_1);
- mono_mb_emit_byte (mb, CEE_LDC_I4_0);
- mono_mb_emit_byte (mb, CEE_PREFIX1);
- mono_mb_emit_byte (mb, CEE_CGT_UN);
- mono_mb_emit_byte (mb, CEE_PREFIX1);
- mono_mb_emit_byte (mb, CEE_ENDFILTER);
+ mono_mb_patch_branch (mb, label1);
+ mono_mb_patch_branch (mb, label3);
+ }
+#endif
- clause->handler_offset = mono_mb_get_label (mb);
+ if (m_class_is_blittable (eklass)) {
+ /* free memory allocated (if any) by MONO_MARSHAL_CONV_ARRAY_LPARRAY */
- /* handler code */
- /* store exception */
- mono_mb_emit_stloc (mb, 1);
+ mono_mb_emit_ldarg (mb, argnum);
+ if (m_type_is_byref (t))
+ mono_mb_emit_byte (mb, CEE_LDIND_REF);
+ mono_mb_emit_ldloc (mb, conv_arg);
+ mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (MONO_MARSHAL_FREE_LPARRAY, NULL));
+ }
- mono_mb_emit_byte (mb, CEE_LDARG_1);
- mono_mb_emit_ldloc (mb, 1);
- mono_mb_emit_byte (mb, CEE_STIND_REF);
+ break;
+ }
- mono_mb_emit_byte (mb, CEE_LDNULL);
- mono_mb_emit_stloc (mb, 0);
+ case MARSHAL_ACTION_PUSH:
+ if (m_type_is_byref (t))
+ mono_mb_emit_ldloc_addr (mb, conv_arg);
+ else
+ mono_mb_emit_ldloc (mb, conv_arg);
+ break;
- mono_mb_emit_branch (mb, CEE_LEAVE);
+ case MARSHAL_ACTION_CONV_RESULT: {
+ mono_mb_emit_byte (mb, CEE_POP);
+ char *msg = g_strdup_printf ("Cannot marshal 'return value': Invalid managed/unmanaged type combination.");
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ break;
+ }
- clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
+ case MARSHAL_ACTION_MANAGED_CONV_IN: {
+ guint32 label1, label2, label3;
+ int index_var, src_ptr, esize, param_num, num_elem;
+ MonoMarshalConv conv;
+ gboolean is_string = FALSE;
- mono_mb_set_clauses (mb, 1, clause);
+ conv_arg = mono_mb_add_local (mb, object_type);
+ *conv_arg_type = int_type;
- /* return result */
- mono_mb_patch_branch (mb, pos);
- //mono_mb_emit_ldloc (mb, 0);
- mono_mb_emit_byte (mb, CEE_RET);
-}
+ if (m_type_is_byref (t)) {
+ char *msg = g_strdup ("Byref array marshalling to managed code is not implemented.");
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ return conv_arg;
+ }
+ if (!spec) {
+ char *msg = g_strdup ("[MarshalAs] attribute required to marshal arrays to managed code.");
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ return conv_arg;
+ }
-typedef struct EmitGCSafeTransitionBuilder {
- MonoMethodBuilder *mb;
- gboolean func_param;
- int coop_gc_var;
+ switch (spec->native) {
+ case MONO_NATIVE_LPARRAY:
+ break;
+ case MONO_NATIVE_SAFEARRAY:
#ifndef DISABLE_COM
- int coop_cominterop_fnptr;
+ if (spec->data.safearray_data.elem_type != MONO_VARIANT_VARIANT) {
+ char *msg = g_strdup ("Only SAFEARRAY(VARIANT) marshalling to managed code is implemented.");
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ return conv_arg;
+ }
+ return mono_cominterop_emit_marshal_safearray (m, argnum, t, spec, conv_arg, conv_arg_type, action);
#endif
-} GCSafeTransitionBuilder;
+ default: {
+ char *msg = g_strdup ("Unsupported array type marshalling to managed code.");
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ return conv_arg;
+ }
+ }
-static gboolean
-gc_safe_transition_builder_init (GCSafeTransitionBuilder *builder, MonoMethodBuilder *mb, gboolean func_param)
-{
- builder->mb = mb;
- builder->func_param = func_param;
- builder->coop_gc_var = -1;
-#ifndef DISABLE_COM
- builder->coop_cominterop_fnptr = -1;
-#endif
-#if defined (TARGET_WASM)
- return FALSE;
-#else
- return TRUE;
-#endif
-}
+ /* FIXME: t is from the method which is wrapped, not the delegate type */
+ /* g_assert (t->attrs & PARAM_ATTRIBUTE_IN); */
-/**
- * adds locals for the gc safe transition to the method builder.
- */
-static void
-gc_safe_transition_builder_add_locals (GCSafeTransitionBuilder *builder)
-{
- MonoType *int_type = mono_get_int_type();
- /* local 4, the local to be used when calling the suspend funcs */
- builder->coop_gc_var = mono_mb_add_local (builder->mb, int_type);
-#ifndef DISABLE_COM
- if (!builder->func_param && MONO_CLASS_IS_IMPORT (builder->mb->method->klass)) {
- builder->coop_cominterop_fnptr = mono_mb_add_local (builder->mb, int_type);
- }
-#endif
-}
+ param_num = spec->data.array_data.param_num;
+ num_elem = spec->data.array_data.num_elem;
+ if (spec->data.array_data.elem_mult == 0)
+ /* param_num is not specified */
+ param_num = -1;
-/**
- * emits
- * cookie = mono_threads_enter_gc_safe_region_unbalanced (ref dummy);
- *
- */
-static void
-gc_safe_transition_builder_emit_enter (GCSafeTransitionBuilder *builder, MonoMethod *method, gboolean aot)
-{
+ if (param_num == -1) {
+ if (num_elem <= 0) {
+ char *msg = g_strdup ("Either SizeConst or SizeParamIndex should be specified when marshalling arrays to managed code.");
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ return conv_arg;
+ }
+ }
- // Perform an extra, early lookup of the function address, so any exceptions
- // potentially resulting from the lookup occur before entering blocking mode.
- if (!builder->func_param && !MONO_CLASS_IS_IMPORT (builder->mb->method->klass) && aot) {
- mono_mb_emit_byte (builder->mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_op (builder->mb, CEE_MONO_ICALL_ADDR, method);
- mono_mb_emit_byte (builder->mb, CEE_POP); // Result not needed yet
- }
+ /* FIXME: Optimize blittable case */
-#ifndef DISABLE_COM
- if (!builder->func_param && MONO_CLASS_IS_IMPORT (builder->mb->method->klass)) {
- mono_mb_emit_cominterop_get_function_pointer (builder->mb, method);
- mono_mb_emit_stloc (builder->mb, builder->coop_cominterop_fnptr);
- }
+#ifndef DISABLE_NONBLITTABLE
+ if (eklass == mono_defaults.string_class) {
+ is_string = TRUE;
+ gboolean need_free;
+ conv = mono_marshal_get_ptr_to_string_conv (m->piinfo, spec, &need_free);
+ }
+ else if (eklass == mono_class_try_get_stringbuilder_class ()) {
+ is_string = TRUE;
+ gboolean need_free;
+ conv = mono_marshal_get_ptr_to_stringbuilder_conv (m->piinfo, spec, &need_free);
+ }
+ else
+ conv = MONO_MARSHAL_CONV_INVALID;
#endif
- mono_mb_emit_byte (builder->mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_byte (builder->mb, CEE_MONO_GET_SP);
- mono_mb_emit_icall (builder->mb, mono_threads_enter_gc_safe_region_unbalanced);
- mono_mb_emit_stloc (builder->mb, builder->coop_gc_var);
-}
+ mono_marshal_load_type_info (eklass);
-/**
- * emits
- * mono_threads_exit_gc_safe_region_unbalanced (cookie, ref dummy);
- *
- */
-static void
-gc_safe_transition_builder_emit_exit (GCSafeTransitionBuilder *builder)
-{
- mono_mb_emit_ldloc (builder->mb, builder->coop_gc_var);
- mono_mb_emit_byte (builder->mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_byte (builder->mb, CEE_MONO_GET_SP);
- mono_mb_emit_icall (builder->mb, mono_threads_exit_gc_safe_region_unbalanced);
-}
-
-static void
-gc_safe_transition_builder_cleanup (GCSafeTransitionBuilder *builder)
-{
- builder->mb = NULL;
- builder->coop_gc_var = -1;
-#ifndef DISABLE_COM
- builder->coop_cominterop_fnptr = -1;
-#endif
-}
-
-static gboolean
-emit_native_wrapper_validate_signature (MonoMethodBuilder *mb, MonoMethodSignature* sig, MonoMarshalSpec** mspecs)
-{
- if (mspecs) {
- for (int i = 0; i < sig->param_count; i ++) {
- if (mspecs [i + 1] && mspecs [i + 1]->native == MONO_NATIVE_CUSTOM) {
- if (!mspecs [i + 1]->data.custom_data.custom_name || *mspecs [i + 1]->data.custom_data.custom_name == '\0') {
- mono_mb_emit_exception_full (mb, "System", "TypeLoadException", g_strdup ("Missing ICustomMarshaler type"));
- return FALSE;
- }
-
- switch (sig->params[i]->type) {
- case MONO_TYPE_CLASS:
- case MONO_TYPE_OBJECT:
- case MONO_TYPE_STRING:
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_VALUETYPE:
- break;
-
- default:
- mono_mb_emit_exception_full (mb, "System.Runtime.InteropServices", "MarshalDirectiveException", g_strdup_printf ("custom marshalling of type %x is currently not supported", sig->params[i]->type));
- return FALSE;
- }
- }
- else if (sig->params[i]->type == MONO_TYPE_VALUETYPE) {
- MonoMarshalType *marshal_type = mono_marshal_load_type_info (mono_class_from_mono_type_internal (sig->params [i]));
- for (int field_idx = 0; field_idx < marshal_type->num_fields; ++field_idx) {
- if (marshal_type->fields [field_idx].mspec && marshal_type->fields [field_idx].mspec->native == MONO_NATIVE_CUSTOM) {
- mono_mb_emit_exception_full (mb, "System", "TypeLoadException", g_strdup ("Value type includes custom marshaled fields"));
- return FALSE;
- }
- }
- }
- }
- }
-
- return TRUE;
-}
-
-/**
- * emit_native_wrapper_ilgen:
- * \param image the image to use for looking up custom marshallers
- * \param sig The signature of the native function
- * \param piinfo Marshalling information
- * \param mspecs Marshalling information
- * \param aot whenever the created method will be compiled by the AOT compiler
- * \param method if non-NULL, the pinvoke method to call
- * \param check_exceptions Whenever to check for pending exceptions after the native call
- * \param func_param the function to call is passed as a boxed IntPtr as the first parameter
- * \param func_param_unboxed combined with \p func_param, expect the function to call as an unboxed IntPtr as the first parameter
- * \param skip_gc_trans Whenever to skip GC transitions
- *
- * generates IL code for the pinvoke wrapper, the generated code calls \p func .
- */
-static void
-emit_native_wrapper_ilgen (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, MonoNativeWrapperFlags flags)
-{
- gboolean aot = (flags & EMIT_NATIVE_WRAPPER_AOT) != 0;
- gboolean check_exceptions = (flags & EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS) != 0;
- gboolean func_param = (flags & EMIT_NATIVE_WRAPPER_FUNC_PARAM) != 0;
- gboolean func_param_unboxed = (flags & EMIT_NATIVE_WRAPPER_FUNC_PARAM_UNBOXED) != 0;
- gboolean skip_gc_trans = (flags & EMIT_NATIVE_WRAPPER_SKIP_GC_TRANS) != 0;
- gboolean runtime_marshalling_enabled = (flags & EMIT_NATIVE_WRAPPER_RUNTIME_MARSHALLING_ENABLED) != 0;
- EmitMarshalContext m;
- MonoMethodSignature *csig;
- MonoClass *klass;
- int i, argnum, *tmp_locals;
- int type, param_shift = 0;
- int func_addr_local = -1;
- gboolean need_gc_safe = FALSE;
- GCSafeTransitionBuilder gc_safe_transition_builder;
-
- memset (&m, 0, sizeof (m));
- m.runtime_marshalling_enabled = runtime_marshalling_enabled;
- m.mb = mb;
- m.sig = sig;
- m.piinfo = piinfo;
-
- if (!emit_native_wrapper_validate_signature (mb, sig, mspecs))
- return;
-
- if (!skip_gc_trans)
- need_gc_safe = gc_safe_transition_builder_init (&gc_safe_transition_builder, mb, func_param);
-
- /* we copy the signature, so that we can set pinvoke to 0 */
- if (func_param) {
- /* The function address is passed as the first argument */
- g_assert (!sig->hasthis);
- param_shift += 1;
- }
- csig = mono_metadata_signature_dup_full (get_method_image (mb->method), sig);
- csig->pinvoke = 1;
- if (!runtime_marshalling_enabled)
- csig->marshalling_disabled = 1;
- m.csig = csig;
- m.image = image;
-
- if (sig->hasthis)
- param_shift += 1;
-
- MonoType *int_type = mono_get_int_type ();
- MonoType *boolean_type = m_class_get_byval_arg (mono_defaults.boolean_class);
- /* we allocate local for use with mono_marshal_shared_emit_struct_conv() */
- /* allocate local 0 (pointer) src_ptr */
- mono_mb_add_local (mb, int_type);
- /* allocate local 1 (pointer) dst_ptr */
- mono_mb_add_local (mb, int_type);
- /* allocate local 2 (boolean) delete_old */
- mono_mb_add_local (mb, boolean_type);
-
- /* delete_old = FALSE */
- mono_mb_emit_icon (mb, 0);
- mono_mb_emit_stloc (mb, 2);
-
- if (!MONO_TYPE_IS_VOID (sig->ret)) {
- /* allocate local 3 to store the return value */
- mono_mb_add_local (mb, sig->ret);
- }
-
- if (need_gc_safe)
- gc_safe_transition_builder_add_locals (&gc_safe_transition_builder);
-
- if (!func && !aot && !func_param && !MONO_CLASS_IS_IMPORT (mb->method->klass)) {
- /*
- * On netcore, its possible to register pinvoke resolvers at runtime, so
- * a pinvoke lookup can fail, and then succeed later. So if the
- * original lookup failed, do a lookup every time until it
- * succeeds.
- * This adds some overhead, but only when the pinvoke lookup
- * was not initially successful.
- * FIXME: AOT case
- */
- func_addr_local = mono_mb_add_local (mb, int_type);
-
- int cache_local = mono_mb_add_local (mb, int_type);
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_op (mb, CEE_MONO_PINVOKE_ADDR_CACHE, &piinfo->method);
- mono_mb_emit_stloc (mb, cache_local);
-
- mono_mb_emit_ldloc (mb, cache_local);
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- int pos = mono_mb_emit_branch (mb, CEE_BRTRUE);
-
- mono_mb_emit_ldloc (mb, cache_local);
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_op (mb, CEE_MONO_METHODCONST, &piinfo->method);
- mono_mb_emit_icall (mb, mono_marshal_lookup_pinvoke);
- mono_mb_emit_byte (mb, CEE_STIND_I);
-
- mono_mb_patch_branch (mb, pos);
- mono_mb_emit_ldloc (mb, cache_local);
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_stloc (mb, func_addr_local);
- }
-
- /*
- * cookie = mono_threads_enter_gc_safe_region_unbalanced (ref dummy);
- *
- * ret = method (...);
- *
- * mono_threads_exit_gc_safe_region_unbalanced (cookie, ref dummy);
- *
- * <interrupt check>
- *
- * return ret;
- */
-
- if (MONO_TYPE_ISSTRUCT (sig->ret))
- m.vtaddr_var = mono_mb_add_local (mb, int_type);
-
- if (mspecs [0] && mspecs [0]->native == MONO_NATIVE_CUSTOM) {
- /* Return type custom marshaling */
- /*
- * Since we can't determine the return type of the unmanaged function,
- * we assume it returns a pointer, and pass that pointer to
- * MarshalNativeToManaged.
- */
- csig->ret = int_type;
- }
-
- // Check if SetLastError usage is valid early so we don't try to throw an exception after transitioning GC modes.
- if (piinfo && (piinfo->piflags & PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR) && !m.runtime_marshalling_enabled)
- mono_marshal_shared_mb_emit_exception_marshal_directive(mb, g_strdup("Setting SetLastError=true is not supported when runtime marshalling is disabled."));
-
- /* we first do all conversions */
- tmp_locals = g_newa (int, sig->param_count);
- m.orig_conv_args = g_newa (int, sig->param_count + 1);
-
- for (i = 0; i < sig->param_count; i ++) {
- tmp_locals [i] = mono_emit_marshal (&m, i + param_shift, sig->params [i], mspecs [i + 1], 0, &csig->params [i], MARSHAL_ACTION_CONV_IN);
- }
-
- // In coop mode need to register blocking state during native call
- if (need_gc_safe)
- gc_safe_transition_builder_emit_enter (&gc_safe_transition_builder, &piinfo->method, aot);
-
- /* push all arguments */
-
- if (sig->hasthis)
- mono_mb_emit_byte (mb, CEE_LDARG_0);
-
- for (i = 0; i < sig->param_count; i++) {
- mono_emit_marshal (&m, i + param_shift, sig->params [i], mspecs [i + 1], tmp_locals [i], NULL, MARSHAL_ACTION_PUSH);
- }
-
- /* call the native method */
- if (func_param) {
- mono_mb_emit_byte (mb, CEE_LDARG_0);
- if (!func_param_unboxed) {
- mono_mb_emit_op (mb, CEE_UNBOX, mono_defaults.int_class);
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- }
- if (piinfo && (piinfo->piflags & PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR) != 0) {
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_byte (mb, CEE_MONO_SAVE_LAST_ERROR);
- }
- mono_mb_emit_calli (mb, csig);
- } else if (MONO_CLASS_IS_IMPORT (mb->method->klass)) {
-#ifndef DISABLE_COM
- mono_mb_emit_ldloc (mb, gc_safe_transition_builder.coop_cominterop_fnptr);
- if (piinfo->piflags & PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR) {
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_byte (mb, CEE_MONO_SAVE_LAST_ERROR);
- }
- mono_mb_emit_cominterop_call_function_pointer (mb, csig);
-#else
- g_assert_not_reached ();
-#endif
- } else {
- if (func_addr_local != -1) {
- mono_mb_emit_ldloc (mb, func_addr_local);
- } else {
- if (aot) {
- /* Reuse the ICALL_ADDR opcode for pinvokes too */
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_op (mb, CEE_MONO_ICALL_ADDR, &piinfo->method);
- }
- }
- if (piinfo->piflags & PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR) {
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_byte (mb, CEE_MONO_SAVE_LAST_ERROR);
- }
- if (func_addr_local != -1 || aot)
- mono_mb_emit_calli (mb, csig);
+ if (is_string)
+ esize = TARGET_SIZEOF_VOID_P;
else
- mono_mb_emit_native_call (mb, csig, func);
- }
-
- if (MONO_TYPE_ISSTRUCT (sig->ret)) {
- klass = mono_class_from_mono_type_internal (sig->ret);
- mono_class_init_internal (klass);
- if (!(mono_class_is_explicit_layout (klass) || m_class_is_blittable (klass))) {
- /* This is used by emit_marshal_vtype (), but it needs to go right before the call */
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_byte (mb, CEE_MONO_VTADDR);
- mono_mb_emit_stloc (mb, m.vtaddr_var);
- }
- }
-
- /* Unblock before converting the result, since that can involve calls into the runtime */
- if (need_gc_safe)
- gc_safe_transition_builder_emit_exit (&gc_safe_transition_builder);
-
- gc_safe_transition_builder_cleanup (&gc_safe_transition_builder);
+ esize = mono_class_native_size (eklass, NULL);
+ src_ptr = mono_mb_add_local (mb, int_type);
- /* convert the result */
- if (!m_type_is_byref (sig->ret)) {
- MonoMarshalSpec *spec = mspecs [0];
- type = sig->ret->type;
+ mono_mb_emit_byte (mb, CEE_LDNULL);
+ mono_mb_emit_stloc (mb, conv_arg);
- if (spec && spec->native == MONO_NATIVE_CUSTOM) {
- mono_emit_marshal (&m, 0, sig->ret, spec, 0, NULL, MARSHAL_ACTION_CONV_RESULT);
- } else {
- handle_enum:
- switch (type) {
- case MONO_TYPE_VOID:
- break;
- case MONO_TYPE_VALUETYPE:
- klass = sig->ret->data.klass;
- if (m_class_is_enumtype (klass)) {
- type = mono_class_enum_basetype_internal (sig->ret->data.klass)->type;
- goto handle_enum;
- }
- mono_emit_marshal (&m, 0, sig->ret, spec, 0, NULL, MARSHAL_ACTION_CONV_RESULT);
- break;
+ /* Check param index */
+ if (param_num != -1) {
+ if (param_num >= m->sig->param_count) {
+ char *msg = g_strdup ("Array size control parameter index is out of range.");
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ return conv_arg;
+ }
+ switch (m->sig->params [param_num]->type) {
case MONO_TYPE_I1:
case MONO_TYPE_U1:
case MONO_TYPE_I2:
case MONO_TYPE_U4:
case MONO_TYPE_I:
case MONO_TYPE_U:
- case MONO_TYPE_R4:
- case MONO_TYPE_R8:
case MONO_TYPE_I8:
case MONO_TYPE_U8:
- case MONO_TYPE_FNPTR:
- case MONO_TYPE_STRING:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_OBJECT:
- case MONO_TYPE_BOOLEAN:
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_CHAR:
- case MONO_TYPE_PTR:
- case MONO_TYPE_GENERICINST:
- mono_emit_marshal (&m, 0, sig->ret, spec, 0, NULL, MARSHAL_ACTION_CONV_RESULT);
break;
- case MONO_TYPE_TYPEDBYREF:
- default:
- g_warning ("return type 0x%02x unknown", sig->ret->type);
- g_assert_not_reached ();
+ default: {
+ char *msg = g_strdup ("Array size control parameter must be an integral type.");
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ return conv_arg;
+ }
}
}
- } else {
- mono_mb_emit_stloc (mb, 3);
- }
- /*
- * Need to call this after converting the result since MONO_VTADDR needs
- * to be adjacent to the call instruction.
- */
- if (check_exceptions)
- emit_thread_interrupt_checkpoint (mb);
-
- /* we need to convert byref arguments back and free string arrays */
- for (i = 0; i < sig->param_count; i++) {
- MonoType *t = sig->params [i];
- MonoMarshalSpec *spec = mspecs [i + 1];
+ /* Check null */
+ mono_mb_emit_ldarg (mb, argnum);
+ label1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
- argnum = i + param_shift;
+ mono_mb_emit_ldarg (mb, argnum);
+ mono_mb_emit_stloc (mb, src_ptr);
- if (spec && ((spec->native == MONO_NATIVE_CUSTOM) || (spec->native == MONO_NATIVE_ASANY))) {
- mono_emit_marshal (&m, argnum, t, spec, tmp_locals [i], NULL, MARSHAL_ACTION_CONV_OUT);
- continue;
- }
+ /* Create managed array */
+ /*
+ * The LPArray marshalling spec says that sometimes param_num starts
+ * from 1, sometimes it starts from 0. But MS seems to allways start
+ * from 0.
+ */
- switch (t->type) {
- case MONO_TYPE_STRING:
- case MONO_TYPE_VALUETYPE:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_OBJECT:
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_BOOLEAN:
- mono_emit_marshal (&m, argnum, t, spec, tmp_locals [i], NULL, MARSHAL_ACTION_CONV_OUT);
- break;
- default:
- break;
+ if (param_num == -1) {
+ mono_mb_emit_icon (mb, num_elem);
+ } else {
+ mono_mb_emit_ldarg (mb, param_num);
+ if (num_elem > 0) {
+ mono_mb_emit_icon (mb, num_elem);
+ mono_mb_emit_byte (mb, CEE_ADD);
+ }
+ mono_mb_emit_byte (mb, CEE_CONV_OVF_I);
}
- }
-
- if (!MONO_TYPE_IS_VOID(sig->ret))
- mono_mb_emit_ldloc (mb, 3);
-
- mono_mb_emit_byte (mb, CEE_RET);
-}
-
-/*
- * The code directly following this is the cache hit, value positive branch
- *
- * This function takes a new method builder with 0 locals and adds two locals
- * to create multiple out-branches and the fall through state of having the object
- * on the stack after a cache miss
- */
-static void
-generate_check_cache (int obj_arg_position, int class_arg_position, int cache_arg_position, // In-parameters
- int *null_obj, int *cache_hit_neg, int *cache_hit_pos, // Out-parameters
- MonoMethodBuilder *mb)
-{
- int cache_miss_pos;
-
- MonoType *int_type = mono_get_int_type ();
- /* allocate local 0 (pointer) obj_vtable */
- mono_mb_add_local (mb, int_type);
- /* allocate local 1 (pointer) cached_vtable */
- mono_mb_add_local (mb, int_type);
-
- /*if (!obj)*/
- mono_mb_emit_ldarg (mb, obj_arg_position);
- *null_obj = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- /*obj_vtable = obj->vtable;*/
- mono_mb_emit_ldarg (mb, obj_arg_position);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable));
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_stloc (mb, 0);
-
- /* cached_vtable = *cache*/
- mono_mb_emit_ldarg (mb, cache_arg_position);
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_stloc (mb, 1);
-
- mono_mb_emit_ldloc (mb, 1);
- mono_mb_emit_byte (mb, CEE_LDC_I4);
- mono_mb_emit_i4 (mb, ~0x1);
- mono_mb_emit_byte (mb, CEE_CONV_I);
- mono_mb_emit_byte (mb, CEE_AND);
- mono_mb_emit_ldloc (mb, 0);
- /*if ((cached_vtable & ~0x1)== obj_vtable)*/
- cache_miss_pos = mono_mb_emit_branch (mb, CEE_BNE_UN);
-
- /*return (cached_vtable & 0x1) ? NULL : obj;*/
- mono_mb_emit_ldloc (mb, 1);
- mono_mb_emit_byte(mb, CEE_LDC_I4_1);
- mono_mb_emit_byte (mb, CEE_CONV_U);
- mono_mb_emit_byte (mb, CEE_AND);
- *cache_hit_neg = mono_mb_emit_branch (mb, CEE_BRTRUE);
- *cache_hit_pos = mono_mb_emit_branch (mb, CEE_BR);
-
- // slow path
- mono_mb_patch_branch (mb, cache_miss_pos);
-
- // if isinst
- mono_mb_emit_ldarg (mb, obj_arg_position);
- mono_mb_emit_ldarg (mb, class_arg_position);
- mono_mb_emit_ldarg (mb, cache_arg_position);
- mono_mb_emit_icall (mb, mono_marshal_isinst_with_cache);
-}
-
-static void
-emit_castclass_ilgen (MonoMethodBuilder *mb)
-{
- int return_null_pos, positive_cache_hit_pos, negative_cache_hit_pos, invalid_cast_pos;
- const int obj_arg_position = TYPECHECK_OBJECT_ARG_POS;
- const int class_arg_position = TYPECHECK_CLASS_ARG_POS;
- const int cache_arg_position = TYPECHECK_CACHE_ARG_POS;
-
- generate_check_cache (obj_arg_position, class_arg_position, cache_arg_position,
- &return_null_pos, &negative_cache_hit_pos, &positive_cache_hit_pos, mb);
- invalid_cast_pos = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- /*return obj;*/
- mono_mb_patch_branch (mb, positive_cache_hit_pos);
- mono_mb_emit_ldarg (mb, obj_arg_position);
- mono_mb_emit_byte (mb, CEE_RET);
-
- /*fails*/
- mono_mb_patch_branch (mb, negative_cache_hit_pos);
- mono_mb_patch_branch (mb, invalid_cast_pos);
- mono_mb_emit_exception (mb, "InvalidCastException", NULL);
-
- /*return null*/
- mono_mb_patch_branch (mb, return_null_pos);
- mono_mb_emit_byte (mb, CEE_LDNULL);
- mono_mb_emit_byte (mb, CEE_RET);
-}
-
-static void
-emit_isinst_ilgen (MonoMethodBuilder *mb)
-{
- int return_null_pos, positive_cache_hit_pos, negative_cache_hit_pos;
- const int obj_arg_position = TYPECHECK_OBJECT_ARG_POS;
- const int class_arg_position = TYPECHECK_CLASS_ARG_POS;
- const int cache_arg_position = TYPECHECK_CACHE_ARG_POS;
-
- generate_check_cache (obj_arg_position, class_arg_position, cache_arg_position,
- &return_null_pos, &negative_cache_hit_pos, &positive_cache_hit_pos, mb);
- // Return the object gotten via the slow path.
- mono_mb_emit_byte (mb, CEE_RET);
-
- // return NULL;
- mono_mb_patch_branch (mb, negative_cache_hit_pos);
- mono_mb_patch_branch (mb, return_null_pos);
- mono_mb_emit_byte (mb, CEE_LDNULL);
- mono_mb_emit_byte (mb, CEE_RET);
-
- // return obj
- mono_mb_patch_branch (mb, positive_cache_hit_pos);
- mono_mb_emit_ldarg (mb, obj_arg_position);
- mono_mb_emit_byte (mb, CEE_RET);
-}
-
-static void
-load_array_element_address (MonoMethodBuilder *mb)
-{
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ldarg (mb, 1);
- mono_mb_emit_op (mb, CEE_LDELEMA, mono_defaults.object_class);
-}
-
-static void
-load_array_class (MonoMethodBuilder *mb, int aklass)
-{
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable));
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_ldflda (mb, m_class_offsetof_element_class ());
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_stloc (mb, aklass);
-}
-
-static void
-load_value_class (MonoMethodBuilder *mb, int vklass)
-{
- mono_mb_emit_ldarg (mb, 2);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable));
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_stloc (mb, vklass);
-}
-
-static int
-emit_marshal_array_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
- MonoMarshalSpec *spec,
- int conv_arg, MonoType **conv_arg_type,
- MarshalAction action)
-{
- MonoMethodBuilder *mb = m->mb;
- MonoClass *klass = mono_class_from_mono_type_internal (t);
- MonoMarshalNative encoding;
-
- encoding = mono_marshal_get_string_encoding (m->piinfo, spec);
- MonoType *int_type = mono_get_int_type ();
- MonoType *object_type = mono_get_object_type ();
-
- MonoClass *eklass = m_class_get_element_class (klass);
- switch (action) {
- case MARSHAL_ACTION_CONV_IN:
- *conv_arg_type = object_type;
- conv_arg = mono_mb_add_local (mb, object_type);
+ mono_mb_emit_op (mb, CEE_NEWARR, eklass);
+ mono_mb_emit_stloc (mb, conv_arg);
if (m_class_is_blittable (eklass)) {
+ mono_mb_emit_ldloc (mb, conv_arg);
+ mono_mb_emit_byte (mb, CEE_CONV_I);
+ mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoArray, vector));
+ mono_mb_emit_byte (mb, CEE_ADD);
mono_mb_emit_ldarg (mb, argnum);
- if (m_type_is_byref (t))
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (MONO_MARSHAL_CONV_ARRAY_LPARRAY, NULL));
- mono_mb_emit_stloc (mb, conv_arg);
- } else {
+ mono_mb_emit_ldloc (mb, conv_arg);
+ mono_mb_emit_byte (mb, CEE_LDLEN);
+ mono_mb_emit_icon (mb, esize);
+ mono_mb_emit_byte (mb, CEE_MUL);
+ mono_mb_emit_byte (mb, CEE_PREFIX1);
+ mono_mb_emit_byte (mb, CEE_CPBLK);
+ mono_mb_patch_branch (mb, label1);
+ break;
+ }
#ifdef DISABLE_NONBLITTABLE
+ else {
char *msg = g_strdup ("Non-blittable marshalling conversion is disabled");
mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ }
#else
- guint32 label1, label2, label3;
- int index_var, src_var, dest_ptr, esize;
- MonoMarshalConv conv;
- gboolean is_string = FALSE;
-
- dest_ptr = mono_mb_add_local (mb, int_type);
+ /* Emit marshalling loop */
+ index_var = mono_mb_add_local (mb, int_type);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_0);
+ mono_mb_emit_stloc (mb, index_var);
+ label2 = mono_mb_get_label (mb);
+ mono_mb_emit_ldloc (mb, index_var);
+ mono_mb_emit_ldloc (mb, conv_arg);
+ mono_mb_emit_byte (mb, CEE_LDLEN);
+ label3 = mono_mb_emit_branch (mb, CEE_BGE);
- if (eklass == mono_defaults.string_class) {
- is_string = TRUE;
- conv = mono_marshal_get_string_to_ptr_conv (m->piinfo, spec);
- }
- else if (eklass == mono_class_try_get_stringbuilder_class ()) {
- is_string = TRUE;
- conv = mono_marshal_get_stringbuilder_to_ptr_conv (m->piinfo, spec);
- }
- else
- conv = MONO_MARSHAL_CONV_INVALID;
-
- if (is_string && conv == MONO_MARSHAL_CONV_INVALID) {
- char *msg = g_strdup_printf ("string/stringbuilder marshalling conversion %d not implemented", encoding);
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- break;
- }
-
- src_var = mono_mb_add_local (mb, object_type);
- mono_mb_emit_ldarg (mb, argnum);
- if (m_type_is_byref (t))
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_stloc (mb, src_var);
-
- /* Check null */
- mono_mb_emit_ldloc (mb, src_var);
- mono_mb_emit_stloc (mb, conv_arg);
- mono_mb_emit_ldloc (mb, src_var);
- label1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- if (is_string)
- esize = TARGET_SIZEOF_VOID_P;
- else if (eklass == mono_defaults.char_class) /*can't call mono_marshal_type_size since it causes all sorts of asserts*/
- esize = mono_pinvoke_is_unicode (m->piinfo) ? 2 : 1;
- else
- esize = mono_class_native_size (eklass, NULL);
-
- /* allocate space for the native struct and store the address */
- mono_mb_emit_icon (mb, esize);
- mono_mb_emit_ldloc (mb, src_var);
- mono_mb_emit_byte (mb, CEE_LDLEN);
-
- if (eklass == mono_defaults.string_class) {
- /* Make the array bigger for the terminating null */
- mono_mb_emit_byte (mb, CEE_LDC_I4_1);
- mono_mb_emit_byte (mb, CEE_ADD);
- }
- mono_mb_emit_byte (mb, CEE_MUL);
- mono_mb_emit_byte (mb, CEE_PREFIX1);
- mono_mb_emit_byte (mb, CEE_LOCALLOC);
- mono_mb_emit_stloc (mb, conv_arg);
-
- mono_mb_emit_ldloc (mb, conv_arg);
- mono_mb_emit_stloc (mb, dest_ptr);
-
- /* Emit marshalling loop */
- index_var = mono_mb_add_local (mb, int_type);
- mono_mb_emit_byte (mb, CEE_LDC_I4_0);
- mono_mb_emit_stloc (mb, index_var);
- label2 = mono_mb_get_label (mb);
- mono_mb_emit_ldloc (mb, index_var);
- mono_mb_emit_ldloc (mb, src_var);
- mono_mb_emit_byte (mb, CEE_LDLEN);
- label3 = mono_mb_emit_branch (mb, CEE_BGE);
-
- /* Emit marshalling code */
-
- if (is_string) {
- int stind_op;
- mono_mb_emit_ldloc (mb, dest_ptr);
- mono_mb_emit_ldloc (mb, src_var);
- mono_mb_emit_ldloc (mb, index_var);
- mono_mb_emit_byte (mb, CEE_LDELEM_REF);
- mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (conv, &stind_op));
- mono_mb_emit_byte (mb, stind_op);
- } else {
- /* set the src_ptr */
- mono_mb_emit_ldloc (mb, src_var);
- mono_mb_emit_ldloc (mb, index_var);
- mono_mb_emit_op (mb, CEE_LDELEMA, eklass);
- mono_mb_emit_stloc (mb, 0);
-
- /* set dst_ptr */
- mono_mb_emit_ldloc (mb, dest_ptr);
- mono_mb_emit_stloc (mb, 1);
-
- /* emit valuetype conversion code */
- mono_marshal_shared_emit_struct_conv_full (mb, eklass, FALSE, 0, eklass == mono_defaults.char_class ? encoding : (MonoMarshalNative)-1);
- }
-
- mono_mb_emit_add_to_local (mb, index_var, 1);
- mono_mb_emit_add_to_local (mb, dest_ptr, esize);
-
- mono_mb_emit_branch_label (mb, CEE_BR, label2);
-
- mono_mb_patch_branch (mb, label3);
-
- if (eklass == mono_defaults.string_class) {
- /* Null terminate */
- mono_mb_emit_ldloc (mb, dest_ptr);
- mono_mb_emit_byte (mb, CEE_LDC_I4_0);
- mono_mb_emit_byte (mb, CEE_STIND_I);
- }
-
- mono_mb_patch_branch (mb, label1);
-#endif
- }
-
- break;
-
- case MARSHAL_ACTION_CONV_OUT: {
-#ifndef DISABLE_NONBLITTABLE
- gboolean need_convert, need_free;
- /* Unicode character arrays are implicitly marshalled as [Out] under MS.NET */
- need_convert = ((eklass == mono_defaults.char_class) && (encoding == MONO_NATIVE_LPWSTR)) || (eklass == mono_class_try_get_stringbuilder_class ()) || (t->attrs & PARAM_ATTRIBUTE_OUT);
- need_free = mono_marshal_need_free (m_class_get_byval_arg (eklass), m->piinfo, spec);
-
- if ((t->attrs & PARAM_ATTRIBUTE_OUT) && spec && spec->native == MONO_NATIVE_LPARRAY && spec->data.array_data.param_num != -1) {
- int param_num = spec->data.array_data.param_num;
- MonoType *param_type;
-
- param_type = m->sig->params [param_num];
-
- if (m_type_is_byref (param_type) && param_type->type != MONO_TYPE_I4) {
- char *msg = g_strdup ("Not implemented.");
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- break;
- }
-
- if (m_type_is_byref (t) ) {
- mono_mb_emit_ldarg (mb, argnum);
-
- /* Create the managed array */
- mono_mb_emit_ldarg (mb, param_num);
- if (m_type_is_byref (m->sig->params [param_num]))
- // FIXME: Support other types
- mono_mb_emit_byte (mb, CEE_LDIND_I4);
- mono_mb_emit_byte (mb, CEE_CONV_OVF_I);
- mono_mb_emit_op (mb, CEE_NEWARR, eklass);
- /* Store into argument */
- mono_mb_emit_byte (mb, CEE_STIND_REF);
- }
- }
-
- if (need_convert || need_free) {
- /* FIXME: Optimize blittable case */
- guint32 label1, label2, label3;
- int index_var, src_ptr, loc, esize;
-
- if ((eklass == mono_class_try_get_stringbuilder_class ()) || (eklass == mono_defaults.string_class))
- esize = TARGET_SIZEOF_VOID_P;
- else if (eklass == mono_defaults.char_class)
- esize = mono_pinvoke_is_unicode (m->piinfo) ? 2 : 1;
- else
- esize = mono_class_native_size (eklass, NULL);
- src_ptr = mono_mb_add_local (mb, int_type);
- loc = mono_mb_add_local (mb, int_type);
-
- /* Check null */
- mono_mb_emit_ldarg (mb, argnum);
- if (m_type_is_byref (t))
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- label1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- mono_mb_emit_ldloc (mb, conv_arg);
- mono_mb_emit_stloc (mb, src_ptr);
-
- /* Emit marshalling loop */
- index_var = mono_mb_add_local (mb, int_type);
- mono_mb_emit_byte (mb, CEE_LDC_I4_0);
- mono_mb_emit_stloc (mb, index_var);
- label2 = mono_mb_get_label (mb);
- mono_mb_emit_ldloc (mb, index_var);
- mono_mb_emit_ldarg (mb, argnum);
- if (m_type_is_byref (t))
- mono_mb_emit_byte (mb, CEE_LDIND_REF);
- mono_mb_emit_byte (mb, CEE_LDLEN);
- label3 = mono_mb_emit_branch (mb, CEE_BGE);
-
- /* Emit marshalling code */
-
- if (eklass == mono_class_try_get_stringbuilder_class ()) {
- gboolean need_free2;
- MonoMarshalConv conv = mono_marshal_get_ptr_to_stringbuilder_conv (m->piinfo, spec, &need_free2);
-
- g_assert (conv != MONO_MARSHAL_CONV_INVALID);
-
- /* dest */
- mono_mb_emit_ldarg (mb, argnum);
- if (m_type_is_byref (t))
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_ldloc (mb, index_var);
- mono_mb_emit_byte (mb, CEE_LDELEM_REF);
-
- /* src */
- mono_mb_emit_ldloc (mb, src_ptr);
- mono_mb_emit_byte (mb, CEE_LDIND_I);
-
- mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (conv, NULL));
-
- if (need_free) {
- /* src */
- mono_mb_emit_ldloc (mb, src_ptr);
- mono_mb_emit_byte (mb, CEE_LDIND_I);
-
- mono_mb_emit_icall (mb, mono_marshal_free);
- }
- }
- else if (eklass == mono_defaults.string_class) {
- if (need_free) {
- /* src */
- mono_mb_emit_ldloc (mb, src_ptr);
- mono_mb_emit_byte (mb, CEE_LDIND_I);
-
- mono_mb_emit_icall (mb, mono_marshal_free);
- }
- }
- else {
- if (need_convert) {
- /* set the src_ptr */
- mono_mb_emit_ldloc (mb, src_ptr);
- mono_mb_emit_stloc (mb, 0);
-
- /* set dst_ptr */
- mono_mb_emit_ldarg (mb, argnum);
- if (m_type_is_byref (t))
- mono_mb_emit_byte (mb, CEE_LDIND_REF);
- mono_mb_emit_ldloc (mb, index_var);
- mono_mb_emit_op (mb, CEE_LDELEMA, eklass);
- mono_mb_emit_stloc (mb, 1);
-
- /* emit valuetype conversion code */
- mono_marshal_shared_emit_struct_conv_full (mb, eklass, TRUE, 0, eklass == mono_defaults.char_class ? encoding : (MonoMarshalNative)-1);
- }
-
- if (need_free) {
- mono_mb_emit_ldloc (mb, src_ptr);
- mono_mb_emit_stloc (mb, loc);
-
- emit_struct_free (mb, eklass, loc);
- }
- }
-
- mono_mb_emit_add_to_local (mb, index_var, 1);
- mono_mb_emit_add_to_local (mb, src_ptr, esize);
-
- mono_mb_emit_branch_label (mb, CEE_BR, label2);
-
- mono_mb_patch_branch (mb, label1);
- mono_mb_patch_branch (mb, label3);
- }
-#endif
-
- if (m_class_is_blittable (eklass)) {
- /* free memory allocated (if any) by MONO_MARSHAL_CONV_ARRAY_LPARRAY */
-
- mono_mb_emit_ldarg (mb, argnum);
- if (m_type_is_byref (t))
- mono_mb_emit_byte (mb, CEE_LDIND_REF);
- mono_mb_emit_ldloc (mb, conv_arg);
- mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (MONO_MARSHAL_FREE_LPARRAY, NULL));
- }
-
- break;
- }
-
- case MARSHAL_ACTION_PUSH:
- if (m_type_is_byref (t))
- mono_mb_emit_ldloc_addr (mb, conv_arg);
- else
- mono_mb_emit_ldloc (mb, conv_arg);
- break;
-
- case MARSHAL_ACTION_CONV_RESULT: {
- mono_mb_emit_byte (mb, CEE_POP);
- char *msg = g_strdup_printf ("Cannot marshal 'return value': Invalid managed/unmanaged type combination.");
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- break;
- }
-
- case MARSHAL_ACTION_MANAGED_CONV_IN: {
- guint32 label1, label2, label3;
- int index_var, src_ptr, esize, param_num, num_elem;
- MonoMarshalConv conv;
- gboolean is_string = FALSE;
-
- conv_arg = mono_mb_add_local (mb, object_type);
- *conv_arg_type = int_type;
-
- if (m_type_is_byref (t)) {
- char *msg = g_strdup ("Byref array marshalling to managed code is not implemented.");
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- return conv_arg;
- }
- if (!spec) {
- char *msg = g_strdup ("[MarshalAs] attribute required to marshal arrays to managed code.");
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- return conv_arg;
- }
-
- switch (spec->native) {
- case MONO_NATIVE_LPARRAY:
- break;
- case MONO_NATIVE_SAFEARRAY:
-#ifndef DISABLE_COM
- if (spec->data.safearray_data.elem_type != MONO_VARIANT_VARIANT) {
- char *msg = g_strdup ("Only SAFEARRAY(VARIANT) marshalling to managed code is implemented.");
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- return conv_arg;
- }
- return mono_cominterop_emit_marshal_safearray (m, argnum, t, spec, conv_arg, conv_arg_type, action);
-#endif
- default: {
- char *msg = g_strdup ("Unsupported array type marshalling to managed code.");
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- return conv_arg;
- }
- }
-
- /* FIXME: t is from the method which is wrapped, not the delegate type */
- /* g_assert (t->attrs & PARAM_ATTRIBUTE_IN); */
-
- param_num = spec->data.array_data.param_num;
- num_elem = spec->data.array_data.num_elem;
- if (spec->data.array_data.elem_mult == 0)
- /* param_num is not specified */
- param_num = -1;
-
- if (param_num == -1) {
- if (num_elem <= 0) {
- char *msg = g_strdup ("Either SizeConst or SizeParamIndex should be specified when marshalling arrays to managed code.");
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- return conv_arg;
- }
- }
-
- /* FIXME: Optimize blittable case */
-
-#ifndef DISABLE_NONBLITTABLE
- if (eklass == mono_defaults.string_class) {
- is_string = TRUE;
- gboolean need_free;
- conv = mono_marshal_get_ptr_to_string_conv (m->piinfo, spec, &need_free);
- }
- else if (eklass == mono_class_try_get_stringbuilder_class ()) {
- is_string = TRUE;
- gboolean need_free;
- conv = mono_marshal_get_ptr_to_stringbuilder_conv (m->piinfo, spec, &need_free);
- }
- else
- conv = MONO_MARSHAL_CONV_INVALID;
-#endif
-
- mono_marshal_load_type_info (eklass);
-
- if (is_string)
- esize = TARGET_SIZEOF_VOID_P;
- else
- esize = mono_class_native_size (eklass, NULL);
- src_ptr = mono_mb_add_local (mb, int_type);
-
- mono_mb_emit_byte (mb, CEE_LDNULL);
- mono_mb_emit_stloc (mb, conv_arg);
-
- /* Check param index */
- if (param_num != -1) {
- if (param_num >= m->sig->param_count) {
- char *msg = g_strdup ("Array size control parameter index is out of range.");
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- return conv_arg;
- }
- switch (m->sig->params [param_num]->type) {
- case MONO_TYPE_I1:
- case MONO_TYPE_U1:
- case MONO_TYPE_I2:
- case MONO_TYPE_U2:
- case MONO_TYPE_I4:
- case MONO_TYPE_U4:
- case MONO_TYPE_I:
- case MONO_TYPE_U:
- case MONO_TYPE_I8:
- case MONO_TYPE_U8:
- break;
- default: {
- char *msg = g_strdup ("Array size control parameter must be an integral type.");
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- return conv_arg;
- }
- }
- }
-
- /* Check null */
- mono_mb_emit_ldarg (mb, argnum);
- label1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- mono_mb_emit_ldarg (mb, argnum);
- mono_mb_emit_stloc (mb, src_ptr);
-
- /* Create managed array */
- /*
- * The LPArray marshalling spec says that sometimes param_num starts
- * from 1, sometimes it starts from 0. But MS seems to allways start
- * from 0.
- */
-
- if (param_num == -1) {
- mono_mb_emit_icon (mb, num_elem);
- } else {
- mono_mb_emit_ldarg (mb, param_num);
- if (num_elem > 0) {
- mono_mb_emit_icon (mb, num_elem);
- mono_mb_emit_byte (mb, CEE_ADD);
- }
- mono_mb_emit_byte (mb, CEE_CONV_OVF_I);
- }
-
- mono_mb_emit_op (mb, CEE_NEWARR, eklass);
- mono_mb_emit_stloc (mb, conv_arg);
-
- if (m_class_is_blittable (eklass)) {
- mono_mb_emit_ldloc (mb, conv_arg);
- mono_mb_emit_byte (mb, CEE_CONV_I);
- mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoArray, vector));
- mono_mb_emit_byte (mb, CEE_ADD);
- mono_mb_emit_ldarg (mb, argnum);
- mono_mb_emit_ldloc (mb, conv_arg);
- mono_mb_emit_byte (mb, CEE_LDLEN);
- mono_mb_emit_icon (mb, esize);
- mono_mb_emit_byte (mb, CEE_MUL);
- mono_mb_emit_byte (mb, CEE_PREFIX1);
- mono_mb_emit_byte (mb, CEE_CPBLK);
- mono_mb_patch_branch (mb, label1);
- break;
- }
-#ifdef DISABLE_NONBLITTABLE
- else {
- char *msg = g_strdup ("Non-blittable marshalling conversion is disabled");
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- }
-#else
- /* Emit marshalling loop */
- index_var = mono_mb_add_local (mb, int_type);
- mono_mb_emit_byte (mb, CEE_LDC_I4_0);
- mono_mb_emit_stloc (mb, index_var);
- label2 = mono_mb_get_label (mb);
- mono_mb_emit_ldloc (mb, index_var);
- mono_mb_emit_ldloc (mb, conv_arg);
- mono_mb_emit_byte (mb, CEE_LDLEN);
- label3 = mono_mb_emit_branch (mb, CEE_BGE);
-
- /* Emit marshalling code */
- if (is_string) {
- g_assert (conv != MONO_MARSHAL_CONV_INVALID);
-
- mono_mb_emit_ldloc (mb, conv_arg);
- mono_mb_emit_ldloc (mb, index_var);
-
- mono_mb_emit_ldloc (mb, src_ptr);
- mono_mb_emit_byte (mb, CEE_LDIND_I);
-
- mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (conv, NULL));
- mono_mb_emit_byte (mb, CEE_STELEM_REF);
- }
- else {
- char *msg = g_strdup ("Marshalling of non-string and non-blittable arrays to managed code is not implemented.");
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- return conv_arg;
- }
-
- mono_mb_emit_add_to_local (mb, index_var, 1);
- mono_mb_emit_add_to_local (mb, src_ptr, esize);
-
- mono_mb_emit_branch_label (mb, CEE_BR, label2);
-
- mono_mb_patch_branch (mb, label1);
- mono_mb_patch_branch (mb, label3);
-#endif
-
- break;
- }
- case MARSHAL_ACTION_MANAGED_CONV_OUT: {
- guint32 label1, label2, label3;
- int index_var, dest_ptr, esize, param_num, num_elem;
- MonoMarshalConv conv;
- gboolean is_string = FALSE;
-
- if (!spec)
- /* Already handled in CONV_IN */
- break;
-
- /* These are already checked in CONV_IN */
- g_assert (!m_type_is_byref (t));
- g_assert (spec->native == MONO_NATIVE_LPARRAY);
- g_assert (t->attrs & PARAM_ATTRIBUTE_OUT);
-
- param_num = spec->data.array_data.param_num;
- num_elem = spec->data.array_data.num_elem;
-
- if (spec->data.array_data.elem_mult == 0)
- /* param_num is not specified */
- param_num = -1;
-
- if (param_num == -1) {
- if (num_elem <= 0) {
- g_assert_not_reached ();
- }
- }
-
- /* FIXME: Optimize blittable case */
-
-#ifndef DISABLE_NONBLITTABLE
- if (eklass == mono_defaults.string_class) {
- is_string = TRUE;
- conv = mono_marshal_get_string_to_ptr_conv (m->piinfo, spec);
- }
- else if (eklass == mono_class_try_get_stringbuilder_class ()) {
- is_string = TRUE;
- conv = mono_marshal_get_stringbuilder_to_ptr_conv (m->piinfo, spec);
- }
- else
- conv = MONO_MARSHAL_CONV_INVALID;
-#endif
-
- mono_marshal_load_type_info (eklass);
-
- if (is_string)
- esize = TARGET_SIZEOF_VOID_P;
- else
- esize = mono_class_native_size (eklass, NULL);
-
- dest_ptr = mono_mb_add_local (mb, int_type);
-
- /* Check null */
- mono_mb_emit_ldloc (mb, conv_arg);
- label1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- mono_mb_emit_ldarg (mb, argnum);
- mono_mb_emit_stloc (mb, dest_ptr);
-
- if (m_class_is_blittable (eklass)) {
- /* dest */
- mono_mb_emit_ldarg (mb, argnum);
- /* src */
- mono_mb_emit_ldloc (mb, conv_arg);
- mono_mb_emit_byte (mb, CEE_CONV_I);
- mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoArray, vector));
- mono_mb_emit_byte (mb, CEE_ADD);
- /* length */
- mono_mb_emit_ldloc (mb, conv_arg);
- mono_mb_emit_byte (mb, CEE_LDLEN);
- mono_mb_emit_icon (mb, esize);
- mono_mb_emit_byte (mb, CEE_MUL);
- mono_mb_emit_byte (mb, CEE_PREFIX1);
- mono_mb_emit_byte (mb, CEE_CPBLK);
- mono_mb_patch_branch (mb, label1);
- break;
- }
-
-#ifndef DISABLE_NONBLITTABLE
- /* Emit marshalling loop */
- index_var = mono_mb_add_local (mb, int_type);
- mono_mb_emit_byte (mb, CEE_LDC_I4_0);
- mono_mb_emit_stloc (mb, index_var);
- label2 = mono_mb_get_label (mb);
- mono_mb_emit_ldloc (mb, index_var);
- mono_mb_emit_ldloc (mb, conv_arg);
- mono_mb_emit_byte (mb, CEE_LDLEN);
- label3 = mono_mb_emit_branch (mb, CEE_BGE);
-
- /* Emit marshalling code */
- if (is_string) {
- int stind_op;
- g_assert (conv != MONO_MARSHAL_CONV_INVALID);
-
- /* dest */
- mono_mb_emit_ldloc (mb, dest_ptr);
-
- /* src */
- mono_mb_emit_ldloc (mb, conv_arg);
- mono_mb_emit_ldloc (mb, index_var);
-
- mono_mb_emit_byte (mb, CEE_LDELEM_REF);
-
- mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (conv, &stind_op));
- mono_mb_emit_byte (mb, stind_op);
- }
- else {
- char *msg = g_strdup ("Marshalling of non-string and non-blittable arrays to managed code is not implemented.");
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- return conv_arg;
- }
-
- mono_mb_emit_add_to_local (mb, index_var, 1);
- mono_mb_emit_add_to_local (mb, dest_ptr, esize);
-
- mono_mb_emit_branch_label (mb, CEE_BR, label2);
-
- mono_mb_patch_branch (mb, label1);
- mono_mb_patch_branch (mb, label3);
-#endif
-
- break;
- }
- case MARSHAL_ACTION_MANAGED_CONV_RESULT: {
-#ifndef DISABLE_NONBLITTABLE
- guint32 label1, label2, label3;
- int index_var, src, dest, esize;
- MonoMarshalConv conv = MONO_MARSHAL_CONV_INVALID;
- gboolean is_string = FALSE;
-
- g_assert (!m_type_is_byref (t));
-
- mono_marshal_load_type_info (eklass);
-
- if (eklass == mono_defaults.string_class) {
- is_string = TRUE;
- conv = mono_marshal_get_string_to_ptr_conv (m->piinfo, spec);
- }
- else {
- g_assert_not_reached ();
- }
-
- if (is_string)
- esize = TARGET_SIZEOF_VOID_P;
- else if (eklass == mono_defaults.char_class)
- esize = mono_pinvoke_is_unicode (m->piinfo) ? 2 : 1;
- else
- esize = mono_class_native_size (eklass, NULL);
-
- src = mono_mb_add_local (mb, object_type);
- dest = mono_mb_add_local (mb, int_type);
-
- mono_mb_emit_stloc (mb, src);
- mono_mb_emit_ldloc (mb, src);
- mono_mb_emit_stloc (mb, 3);
-
- /* Check for null */
- mono_mb_emit_ldloc (mb, src);
- label1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- /* Allocate native array */
- mono_mb_emit_icon (mb, esize);
- mono_mb_emit_ldloc (mb, src);
- mono_mb_emit_byte (mb, CEE_LDLEN);
-
- if (eklass == mono_defaults.string_class) {
- /* Make the array bigger for the terminating null */
- mono_mb_emit_byte (mb, CEE_LDC_I4_1);
- mono_mb_emit_byte (mb, CEE_ADD);
- }
- mono_mb_emit_byte (mb, CEE_MUL);
- mono_mb_emit_icall (mb, ves_icall_marshal_alloc);
- mono_mb_emit_stloc (mb, dest);
- mono_mb_emit_ldloc (mb, dest);
- mono_mb_emit_stloc (mb, 3);
-
- /* Emit marshalling loop */
- index_var = mono_mb_add_local (mb, int_type);
- mono_mb_emit_byte (mb, CEE_LDC_I4_0);
- mono_mb_emit_stloc (mb, index_var);
- label2 = mono_mb_get_label (mb);
- mono_mb_emit_ldloc (mb, index_var);
- mono_mb_emit_ldloc (mb, src);
- mono_mb_emit_byte (mb, CEE_LDLEN);
- label3 = mono_mb_emit_branch (mb, CEE_BGE);
-
- /* Emit marshalling code */
- if (is_string) {
- int stind_op;
- g_assert (conv != MONO_MARSHAL_CONV_INVALID);
-
- /* dest */
- mono_mb_emit_ldloc (mb, dest);
-
- /* src */
- mono_mb_emit_ldloc (mb, src);
- mono_mb_emit_ldloc (mb, index_var);
-
- mono_mb_emit_byte (mb, CEE_LDELEM_REF);
-
- mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (conv, &stind_op));
- mono_mb_emit_byte (mb, stind_op);
- }
- else {
- char *msg = g_strdup ("Marshalling of non-string arrays to managed code is not implemented.");
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- return conv_arg;
- }
-
- mono_mb_emit_add_to_local (mb, index_var, 1);
- mono_mb_emit_add_to_local (mb, dest, esize);
-
- mono_mb_emit_branch_label (mb, CEE_BR, label2);
-
- mono_mb_patch_branch (mb, label3);
- mono_mb_patch_branch (mb, label1);
-#endif
- break;
- }
- default:
- g_assert_not_reached ();
- }
- return conv_arg;
-}
-
-static int
-emit_marshal_ptr_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
- MonoMarshalSpec *spec, int conv_arg,
- MonoType **conv_arg_type, MarshalAction action)
-{
- MonoMethodBuilder *mb = m->mb;
-
- switch (action) {
- case MARSHAL_ACTION_CONV_IN:
- /* MS seems to allow this in some cases, ie. bxc #158 */
- /*
- if (MONO_TYPE_ISSTRUCT (t->data.type) && !mono_class_from_mono_type_internal (t->data.type)->blittable) {
- char *msg = g_strdup_printf ("Can not marshal 'parameter #%d': Pointers can not reference marshaled structures. Use byref instead.", argnum + 1);
- mono_marshal_shared_mb_emit_exception_marshal_directive (m->mb, msg);
- }
- */
- break;
-
- case MARSHAL_ACTION_PUSH:
- mono_mb_emit_ldarg (mb, argnum);
- break;
-
- case MARSHAL_ACTION_CONV_RESULT:
- /* no conversions necessary */
- mono_mb_emit_stloc (mb, 3);
- break;
-
- default:
- break;
- }
- return conv_arg;
-}
-
-static int
-emit_marshal_scalar_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
- MonoMarshalSpec *spec, int conv_arg,
- MonoType **conv_arg_type, MarshalAction action)
-{
- MonoMethodBuilder *mb = m->mb;
-
- switch (action) {
- case MARSHAL_ACTION_PUSH:
- mono_mb_emit_ldarg (mb, argnum);
- break;
-
- case MARSHAL_ACTION_CONV_RESULT:
- /* no conversions necessary */
- mono_mb_emit_stloc (mb, 3);
- break;
-
- case MARSHAL_ACTION_MANAGED_CONV_RESULT:
- mono_mb_emit_stloc (mb, 3);
- break;
-
- default:
- break;
- }
- return conv_arg;
-}
-
-static int
-emit_marshal_boolean_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
- MonoMarshalSpec *spec,
- int conv_arg, MonoType **conv_arg_type,
- MarshalAction action)
-{
- MonoMethodBuilder *mb = m->mb;
- MonoType *int_type = mono_get_int_type ();
- MonoType *boolean_type = m_class_get_byval_arg (mono_defaults.boolean_class);
-
- switch (action) {
- case MARSHAL_ACTION_CONV_IN: {
- MonoType *local_type;
- int label_false;
- guint8 ldc_op = CEE_LDC_I4_1;
-
- local_type = mono_marshal_boolean_conv_in_get_local_type (spec, &ldc_op);
- if (m_type_is_byref (t))
- *conv_arg_type = int_type;
- else
- *conv_arg_type = local_type;
- conv_arg = mono_mb_add_local (mb, local_type);
-
- mono_mb_emit_ldarg (mb, argnum);
- if (m_type_is_byref (t))
- mono_mb_emit_byte (mb, CEE_LDIND_I1);
- label_false = mono_mb_emit_branch (mb, CEE_BRFALSE);
- mono_mb_emit_byte (mb, ldc_op);
- mono_mb_emit_stloc (mb, conv_arg);
- mono_mb_patch_branch (mb, label_false);
-
- break;
- }
-
- case MARSHAL_ACTION_CONV_OUT:
- {
- int label_false, label_end;
- if (!m_type_is_byref (t))
- break;
-
- mono_mb_emit_ldarg (mb, argnum);
- mono_mb_emit_ldloc (mb, conv_arg);
-
- label_false = mono_mb_emit_branch (mb, CEE_BRFALSE);
- mono_mb_emit_byte (mb, CEE_LDC_I4_1);
-
- label_end = mono_mb_emit_branch (mb, CEE_BR);
- mono_mb_patch_branch (mb, label_false);
- mono_mb_emit_byte (mb, CEE_LDC_I4_0);
- mono_mb_patch_branch (mb, label_end);
-
- mono_mb_emit_byte (mb, CEE_STIND_I1);
- break;
- }
-
- case MARSHAL_ACTION_PUSH:
- if (m_type_is_byref (t))
- mono_mb_emit_ldloc_addr (mb, conv_arg);
- else if (conv_arg)
- mono_mb_emit_ldloc (mb, conv_arg);
- else
- mono_mb_emit_ldarg (mb, argnum);
- break;
-
- case MARSHAL_ACTION_CONV_RESULT:
- /* maybe we need to make sure that it fits within 8 bits */
- mono_mb_emit_stloc (mb, 3);
- break;
-
- case MARSHAL_ACTION_MANAGED_CONV_IN: {
- MonoClass* conv_arg_class = mono_defaults.int32_class;
- guint8 ldop = CEE_LDIND_I4;
- int label_null, label_false;
-
- conv_arg_class = mono_marshal_boolean_managed_conv_in_get_conv_arg_class (spec, &ldop);
- conv_arg = mono_mb_add_local (mb, boolean_type);
-
- if (m_type_is_byref (t))
- *conv_arg_type = m_class_get_this_arg (conv_arg_class);
- else
- *conv_arg_type = m_class_get_byval_arg (conv_arg_class);
-
-
- mono_mb_emit_ldarg (mb, argnum);
-
- /* Check null */
- if (m_type_is_byref (t)) {
- label_null = mono_mb_emit_branch (mb, CEE_BRFALSE);
- mono_mb_emit_ldarg (mb, argnum);
- mono_mb_emit_byte (mb, ldop);
- } else
- label_null = 0;
-
- label_false = mono_mb_emit_branch (mb, CEE_BRFALSE);
- mono_mb_emit_byte (mb, CEE_LDC_I4_1);
- mono_mb_emit_stloc (mb, conv_arg);
- mono_mb_patch_branch (mb, label_false);
-
- if (m_type_is_byref (t))
- mono_mb_patch_branch (mb, label_null);
- break;
- }
-
- case MARSHAL_ACTION_MANAGED_CONV_OUT: {
- guint8 stop = CEE_STIND_I4;
- guint8 ldc_op = CEE_LDC_I4_1;
- int label_null,label_false, label_end;
-
- if (!m_type_is_byref (t))
- break;
- if (spec) {
- switch (spec->native) {
- case MONO_NATIVE_I1:
- case MONO_NATIVE_U1:
- stop = CEE_STIND_I1;
- break;
- case MONO_NATIVE_VARIANTBOOL:
- stop = CEE_STIND_I2;
- ldc_op = CEE_LDC_I4_M1;
- break;
- default:
- break;
- }
- }
-
- /* Check null */
- mono_mb_emit_ldarg (mb, argnum);
- label_null = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- mono_mb_emit_ldarg (mb, argnum);
- mono_mb_emit_ldloc (mb, conv_arg);
-
- label_false = mono_mb_emit_branch (mb, CEE_BRFALSE);
- mono_mb_emit_byte (mb, ldc_op);
- label_end = mono_mb_emit_branch (mb, CEE_BR);
-
- mono_mb_patch_branch (mb, label_false);
- mono_mb_emit_byte (mb, CEE_LDC_I4_0);
- mono_mb_patch_branch (mb, label_end);
-
- mono_mb_emit_byte (mb, stop);
- mono_mb_patch_branch (mb, label_null);
- break;
- }
-
- default:
- g_assert_not_reached ();
- }
- return conv_arg;
-}
-
-static int
-emit_marshal_char_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
- MonoMarshalSpec *spec, int conv_arg,
- MonoType **conv_arg_type, MarshalAction action)
-{
- MonoMethodBuilder *mb = m->mb;
-
- switch (action) {
- case MARSHAL_ACTION_PUSH:
- /* fixme: dont know how to marshal that. We cant simply
- * convert it to a one byte UTF8 character, because an
- * unicode character may need more that one byte in UTF8 */
- mono_mb_emit_ldarg (mb, argnum);
- break;
-
- case MARSHAL_ACTION_CONV_RESULT:
- /* fixme: we need conversions here */
- mono_mb_emit_stloc (mb, 3);
- break;
-
- default:
- break;
- }
- return conv_arg;
-}
-
-static void
-emit_virtual_stelemref_ilgen (MonoMethodBuilder *mb, const char **param_names, MonoStelemrefKind kind)
-{
- guint32 b1, b2, b3, b4;
- int aklass, vklass, vtable, uiid;
- int array_slot_addr;
-
- mono_mb_set_param_names (mb, param_names);
- MonoType *int_type = mono_get_int_type ();
- MonoType *int32_type = m_class_get_byval_arg (mono_defaults.int32_class);
- MonoType *object_type_byref = mono_class_get_byref_type (mono_defaults.object_class);
-
- /*For now simply call plain old stelemref*/
- switch (kind) {
- case STELEMREF_OBJECT:
- /* ldelema (implicit bound check) */
- load_array_element_address (mb);
- /* do_store */
- mono_mb_emit_ldarg (mb, 2);
- mono_mb_emit_byte (mb, CEE_STIND_REF);
- mono_mb_emit_byte (mb, CEE_RET);
- break;
-
- case STELEMREF_COMPLEX: {
- int b_fast;
- /*
- <ldelema (bound check)>
- if (!value)
- goto store;
- if (!mono_object_isinst (value, aklass))
- goto do_exception;
-
- do_store:
- *array_slot_addr = value;
-
- do_exception:
- throw new ArrayTypeMismatchException ();
- */
-
- aklass = mono_mb_add_local (mb, int_type);
- vklass = mono_mb_add_local (mb, int_type);
- array_slot_addr = mono_mb_add_local (mb, object_type_byref);
-
-#if 0
- {
- /*Use this to debug/record stores that are going thru the slow path*/
- MonoMethodSignature *csig;
- csig = mono_metadata_signature_alloc (mono_defaults.corlib, 3);
- csig->ret = mono_get_void_type ();
- csig->params [0] = object_type;
- csig->params [1] = int_type; /* this is a natural sized int */
- csig->params [2] = object_type;
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ldarg (mb, 1);
- mono_mb_emit_ldarg (mb, 2);
- mono_mb_emit_native_call (mb, csig, record_slot_vstore);
- }
-#endif
-
- /* ldelema (implicit bound check) */
- load_array_element_address (mb);
- mono_mb_emit_stloc (mb, array_slot_addr);
-
- /* if (!value) goto do_store */
- mono_mb_emit_ldarg (mb, 2);
- b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- /* aklass = array->vtable->klass->element_class */
- load_array_class (mb, aklass);
- /* vklass = value->vtable->klass */
- load_value_class (mb, vklass);
-
- /* fastpath */
- mono_mb_emit_ldloc (mb, vklass);
- mono_mb_emit_ldloc (mb, aklass);
- b_fast = mono_mb_emit_branch (mb, CEE_BEQ);
-
- /*if (mono_object_isinst (value, aklass)) */
- mono_mb_emit_ldarg (mb, 2);
- mono_mb_emit_ldloc (mb, aklass);
- mono_mb_emit_icall (mb, mono_object_isinst_icall);
- b2 = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- /* do_store: */
- mono_mb_patch_branch (mb, b1);
- mono_mb_patch_branch (mb, b_fast);
- mono_mb_emit_ldloc (mb, array_slot_addr);
- mono_mb_emit_ldarg (mb, 2);
- mono_mb_emit_byte (mb, CEE_STIND_REF);
- mono_mb_emit_byte (mb, CEE_RET);
-
- /* do_exception: */
- mono_mb_patch_branch (mb, b2);
-
- mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
- break;
- }
- case STELEMREF_SEALED_CLASS:
- /*
- <ldelema (bound check)>
- if (!value)
- goto store;
-
- aklass = array->vtable->m_class_get_element_class (klass);
- vklass = value->vtable->klass;
-
- if (vklass != aklass)
- goto do_exception;
-
- do_store:
- *array_slot_addr = value;
-
- do_exception:
- throw new ArrayTypeMismatchException ();
- */
- aklass = mono_mb_add_local (mb, int_type);
- vklass = mono_mb_add_local (mb, int_type);
- array_slot_addr = mono_mb_add_local (mb, object_type_byref);
-
- /* ldelema (implicit bound check) */
- load_array_element_address (mb);
- mono_mb_emit_stloc (mb, array_slot_addr);
-
- /* if (!value) goto do_store */
- mono_mb_emit_ldarg (mb, 2);
- b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- /* aklass = array->vtable->klass->element_class */
- load_array_class (mb, aklass);
-
- /* vklass = value->vtable->klass */
- load_value_class (mb, vklass);
-
- /*if (vklass != aklass) goto do_exception; */
- mono_mb_emit_ldloc (mb, aklass);
- mono_mb_emit_ldloc (mb, vklass);
- b2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
-
- /* do_store: */
- mono_mb_patch_branch (mb, b1);
- mono_mb_emit_ldloc (mb, array_slot_addr);
- mono_mb_emit_ldarg (mb, 2);
- mono_mb_emit_byte (mb, CEE_STIND_REF);
- mono_mb_emit_byte (mb, CEE_RET);
-
- /* do_exception: */
- mono_mb_patch_branch (mb, b2);
- mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
- break;
-
- case STELEMREF_CLASS: {
- /*
- the method:
- <ldelema (bound check)>
- if (!value)
- goto do_store;
-
- aklass = array->vtable->m_class_get_element_class (klass);
- vklass = value->vtable->klass;
-
- if (vklass->idepth < aklass->idepth)
- goto do_exception;
-
- if (vklass->supertypes [aklass->idepth - 1] != aklass)
- goto do_exception;
-
- do_store:
- *array_slot_addr = value;
- return;
-
- long:
- throw new ArrayTypeMismatchException ();
- */
- aklass = mono_mb_add_local (mb, int_type);
- vklass = mono_mb_add_local (mb, int_type);
- array_slot_addr = mono_mb_add_local (mb, object_type_byref);
-
- /* ldelema (implicit bound check) */
- load_array_element_address (mb);
- mono_mb_emit_stloc (mb, array_slot_addr);
-
- /* if (!value) goto do_store */
- mono_mb_emit_ldarg (mb, 2);
- b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- /* aklass = array->vtable->klass->element_class */
- load_array_class (mb, aklass);
-
- /* vklass = value->vtable->klass */
- load_value_class (mb, vklass);
-
- /* if (vklass->idepth < aklass->idepth) goto failue */
- mono_mb_emit_ldloc (mb, vklass);
- mono_mb_emit_ldflda (mb, m_class_offsetof_idepth ());
- mono_mb_emit_byte (mb, CEE_LDIND_U2);
-
- mono_mb_emit_ldloc (mb, aklass);
- mono_mb_emit_ldflda (mb, m_class_offsetof_idepth ());
- mono_mb_emit_byte (mb, CEE_LDIND_U2);
+ /* Emit marshalling code */
+ if (is_string) {
+ g_assert (conv != MONO_MARSHAL_CONV_INVALID);
- b3 = mono_mb_emit_branch (mb, CEE_BLT_UN);
+ mono_mb_emit_ldloc (mb, conv_arg);
+ mono_mb_emit_ldloc (mb, index_var);
- /* if (vklass->supertypes [aklass->idepth - 1] != aklass) goto failure */
- mono_mb_emit_ldloc (mb, vklass);
- mono_mb_emit_ldflda (mb, m_class_offsetof_supertypes ());
- mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_ldloc (mb, src_ptr);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_ldloc (mb, aklass);
- mono_mb_emit_ldflda (mb, m_class_offsetof_idepth ());
- mono_mb_emit_byte (mb, CEE_LDIND_U2);
- mono_mb_emit_icon (mb, 1);
- mono_mb_emit_byte (mb, CEE_SUB);
- mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P);
- mono_mb_emit_byte (mb, CEE_MUL);
- mono_mb_emit_byte (mb, CEE_ADD);
- mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (conv, NULL));
+ mono_mb_emit_byte (mb, CEE_STELEM_REF);
+ }
+ else {
+ char *msg = g_strdup ("Marshalling of non-string and non-blittable arrays to managed code is not implemented.");
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ return conv_arg;
+ }
- mono_mb_emit_ldloc (mb, aklass);
- b4 = mono_mb_emit_branch (mb, CEE_BNE_UN);
+ mono_mb_emit_add_to_local (mb, index_var, 1);
+ mono_mb_emit_add_to_local (mb, src_ptr, esize);
- /* do_store: */
- mono_mb_patch_branch (mb, b1);
- mono_mb_emit_ldloc (mb, array_slot_addr);
- mono_mb_emit_ldarg (mb, 2);
- mono_mb_emit_byte (mb, CEE_STIND_REF);
- mono_mb_emit_byte (mb, CEE_RET);
+ mono_mb_emit_branch_label (mb, CEE_BR, label2);
- /* do_exception: */
- mono_mb_patch_branch (mb, b3);
- mono_mb_patch_branch (mb, b4);
+ mono_mb_patch_branch (mb, label1);
+ mono_mb_patch_branch (mb, label3);
+#endif
- mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
break;
}
+ case MARSHAL_ACTION_MANAGED_CONV_OUT: {
+ guint32 label1, label2, label3;
+ int index_var, dest_ptr, esize, param_num, num_elem;
+ MonoMarshalConv conv;
+ gboolean is_string = FALSE;
- case STELEMREF_CLASS_SMALL_IDEPTH:
- /*
- the method:
- <ldelema (bound check)>
- if (!value)
- goto do_store;
-
- aklass = array->vtable->m_class_get_element_class (klass);
- vklass = value->vtable->klass;
-
- if (vklass->supertypes [aklass->idepth - 1] != aklass)
- goto do_exception;
+ if (!spec)
+ /* Already handled in CONV_IN */
+ break;
- do_store:
- *array_slot_addr = value;
- return;
+ /* These are already checked in CONV_IN */
+ g_assert (!m_type_is_byref (t));
+ g_assert (spec->native == MONO_NATIVE_LPARRAY);
+ g_assert (t->attrs & PARAM_ATTRIBUTE_OUT);
- long:
- throw new ArrayTypeMismatchException ();
- */
- aklass = mono_mb_add_local (mb, int_type);
- vklass = mono_mb_add_local (mb, int_type);
- array_slot_addr = mono_mb_add_local (mb, object_type_byref);
+ param_num = spec->data.array_data.param_num;
+ num_elem = spec->data.array_data.num_elem;
- /* ldelema (implicit bound check) */
- load_array_element_address (mb);
- mono_mb_emit_stloc (mb, array_slot_addr);
+ if (spec->data.array_data.elem_mult == 0)
+ /* param_num is not specified */
+ param_num = -1;
- /* if (!value) goto do_store */
- mono_mb_emit_ldarg (mb, 2);
- b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+ if (param_num == -1) {
+ if (num_elem <= 0) {
+ g_assert_not_reached ();
+ }
+ }
- /* aklass = array->vtable->klass->element_class */
- load_array_class (mb, aklass);
+ /* FIXME: Optimize blittable case */
- /* vklass = value->vtable->klass */
- load_value_class (mb, vklass);
+#ifndef DISABLE_NONBLITTABLE
+ if (eklass == mono_defaults.string_class) {
+ is_string = TRUE;
+ conv = mono_marshal_get_string_to_ptr_conv (m->piinfo, spec);
+ }
+ else if (eklass == mono_class_try_get_stringbuilder_class ()) {
+ is_string = TRUE;
+ conv = mono_marshal_get_stringbuilder_to_ptr_conv (m->piinfo, spec);
+ }
+ else
+ conv = MONO_MARSHAL_CONV_INVALID;
+#endif
- /* if (vklass->supertypes [aklass->idepth - 1] != aklass) goto failure */
- mono_mb_emit_ldloc (mb, vklass);
- mono_mb_emit_ldflda (mb, m_class_offsetof_supertypes ());
- mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_marshal_load_type_info (eklass);
- mono_mb_emit_ldloc (mb, aklass);
- mono_mb_emit_ldflda (mb, m_class_offsetof_idepth ());
- mono_mb_emit_byte (mb, CEE_LDIND_U2);
- mono_mb_emit_icon (mb, 1);
- mono_mb_emit_byte (mb, CEE_SUB);
- mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P);
- mono_mb_emit_byte (mb, CEE_MUL);
- mono_mb_emit_byte (mb, CEE_ADD);
- mono_mb_emit_byte (mb, CEE_LDIND_I);
+ if (is_string)
+ esize = TARGET_SIZEOF_VOID_P;
+ else
+ esize = mono_class_native_size (eklass, NULL);
- mono_mb_emit_ldloc (mb, aklass);
- b4 = mono_mb_emit_branch (mb, CEE_BNE_UN);
+ dest_ptr = mono_mb_add_local (mb, int_type);
- /* do_store: */
- mono_mb_patch_branch (mb, b1);
- mono_mb_emit_ldloc (mb, array_slot_addr);
- mono_mb_emit_ldarg (mb, 2);
- mono_mb_emit_byte (mb, CEE_STIND_REF);
- mono_mb_emit_byte (mb, CEE_RET);
+ /* Check null */
+ mono_mb_emit_ldloc (mb, conv_arg);
+ label1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
- /* do_exception: */
- mono_mb_patch_branch (mb, b4);
+ mono_mb_emit_ldarg (mb, argnum);
+ mono_mb_emit_stloc (mb, dest_ptr);
- mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
- break;
+ if (m_class_is_blittable (eklass)) {
+ /* dest */
+ mono_mb_emit_ldarg (mb, argnum);
+ /* src */
+ mono_mb_emit_ldloc (mb, conv_arg);
+ mono_mb_emit_byte (mb, CEE_CONV_I);
+ mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoArray, vector));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ /* length */
+ mono_mb_emit_ldloc (mb, conv_arg);
+ mono_mb_emit_byte (mb, CEE_LDLEN);
+ mono_mb_emit_icon (mb, esize);
+ mono_mb_emit_byte (mb, CEE_MUL);
+ mono_mb_emit_byte (mb, CEE_PREFIX1);
+ mono_mb_emit_byte (mb, CEE_CPBLK);
+ mono_mb_patch_branch (mb, label1);
+ break;
+ }
- case STELEMREF_INTERFACE:
- /*Mono *klass;
- MonoVTable *vt;
- unsigned uiid;
- if (value == NULL)
- goto store;
-
- klass = array->obj.vtable->klass->element_class;
- vt = value->vtable;
- uiid = klass->interface_id;
- if (uiid > vt->max_interface_id)
- goto exception;
- if (!(vt->interface_bitmap [(uiid) >> 3] & (1 << ((uiid)&7))))
- goto exception;
- store:
- mono_array_setref_internal (array, index, value);
- return;
- exception:
- mono_raise_exception (mono_get_exception_array_type_mismatch ());*/
-
- array_slot_addr = mono_mb_add_local (mb, object_type_byref);
- aklass = mono_mb_add_local (mb, int_type);
- vtable = mono_mb_add_local (mb, int_type);
- uiid = mono_mb_add_local (mb, int32_type);
-
- /* ldelema (implicit bound check) */
- load_array_element_address (mb);
- mono_mb_emit_stloc (mb, array_slot_addr);
-
- /* if (!value) goto do_store */
- mono_mb_emit_ldarg (mb, 2);
- b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- /* klass = array->vtable->m_class_get_element_class (klass) */
- load_array_class (mb, aklass);
-
- /* vt = value->vtable */
- mono_mb_emit_ldarg (mb, 2);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable));
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_stloc (mb, vtable);
-
- /* uiid = klass->interface_id; */
- mono_mb_emit_ldloc (mb, aklass);
- mono_mb_emit_ldflda (mb, m_class_offsetof_interface_id ());
- mono_mb_emit_byte (mb, CEE_LDIND_U4);
- mono_mb_emit_stloc (mb, uiid);
-
- /*if (uiid > vt->max_interface_id)*/
- mono_mb_emit_ldloc (mb, uiid);
- mono_mb_emit_ldloc (mb, vtable);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, max_interface_id));
- mono_mb_emit_byte (mb, CEE_LDIND_U4);
- b2 = mono_mb_emit_branch (mb, CEE_BGT_UN);
-
- /* if (!(vt->interface_bitmap [(uiid) >> 3] & (1 << ((uiid)&7)))) */
-
- /*vt->interface_bitmap*/
- mono_mb_emit_ldloc (mb, vtable);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, interface_bitmap));
- mono_mb_emit_byte (mb, CEE_LDIND_I);
+#ifndef DISABLE_NONBLITTABLE
+ /* Emit marshalling loop */
+ index_var = mono_mb_add_local (mb, int_type);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_0);
+ mono_mb_emit_stloc (mb, index_var);
+ label2 = mono_mb_get_label (mb);
+ mono_mb_emit_ldloc (mb, index_var);
+ mono_mb_emit_ldloc (mb, conv_arg);
+ mono_mb_emit_byte (mb, CEE_LDLEN);
+ label3 = mono_mb_emit_branch (mb, CEE_BGE);
- /*uiid >> 3*/
- mono_mb_emit_ldloc (mb, uiid);
- mono_mb_emit_icon (mb, 3);
- mono_mb_emit_byte (mb, CEE_SHR_UN);
-
- /*vt->interface_bitmap [(uiid) >> 3]*/
- mono_mb_emit_byte (mb, CEE_ADD); /*interface_bitmap is a guint8 array*/
- mono_mb_emit_byte (mb, CEE_LDIND_U1);
-
- /*(1 << ((uiid)&7)))*/
- mono_mb_emit_icon (mb, 1);
- mono_mb_emit_ldloc (mb, uiid);
- mono_mb_emit_icon (mb, 7);
- mono_mb_emit_byte (mb, CEE_AND);
- mono_mb_emit_byte (mb, CEE_SHL);
-
- /*bitwise and the whole thing*/
- mono_mb_emit_byte (mb, CEE_AND);
- b3 = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- /* do_store: */
- mono_mb_patch_branch (mb, b1);
- mono_mb_emit_ldloc (mb, array_slot_addr);
- mono_mb_emit_ldarg (mb, 2);
- mono_mb_emit_byte (mb, CEE_STIND_REF);
- mono_mb_emit_byte (mb, CEE_RET);
-
- /* do_exception: */
- mono_mb_patch_branch (mb, b2);
- mono_mb_patch_branch (mb, b3);
- mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
- break;
+ /* Emit marshalling code */
+ if (is_string) {
+ int stind_op;
+ g_assert (conv != MONO_MARSHAL_CONV_INVALID);
- default:
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ldarg (mb, 1);
- mono_mb_emit_ldarg (mb, 2);
- mono_mb_emit_managed_call (mb, mono_marshal_get_stelemref (), NULL);
- mono_mb_emit_byte (mb, CEE_RET);
- g_assert (0);
- }
-}
+ /* dest */
+ mono_mb_emit_ldloc (mb, dest_ptr);
-static void
-emit_stelemref_ilgen (MonoMethodBuilder *mb)
-{
- guint32 b1, b2, b3, b4;
- guint32 copy_pos;
- int aklass, vklass;
- int array_slot_addr;
+ /* src */
+ mono_mb_emit_ldloc (mb, conv_arg);
+ mono_mb_emit_ldloc (mb, index_var);
- MonoType *int_type = mono_get_int_type ();
- MonoType *object_type_byref = mono_class_get_byref_type (mono_defaults.object_class);
-
- aklass = mono_mb_add_local (mb, int_type);
- vklass = mono_mb_add_local (mb, int_type);
- array_slot_addr = mono_mb_add_local (mb, object_type_byref);
-
- /*
- the method:
- <ldelema (bound check)>
- if (!value)
- goto store;
-
- aklass = array->vtable->m_class_get_element_class (klass);
- vklass = value->vtable->klass;
-
- if (vklass->idepth < aklass->idepth)
- goto long;
-
- if (vklass->supertypes [aklass->idepth - 1] != aklass)
- goto long;
-
- store:
- *array_slot_addr = value;
- return;
-
- long:
- if (mono_object_isinst (value, aklass))
- goto store;
-
- throw new ArrayTypeMismatchException ();
- */
-
- /* ldelema (implicit bound check) */
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ldarg (mb, 1);
- mono_mb_emit_op (mb, CEE_LDELEMA, mono_defaults.object_class);
- mono_mb_emit_stloc (mb, array_slot_addr);
-
- /* if (!value) goto do_store */
- mono_mb_emit_ldarg (mb, 2);
- b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- /* aklass = array->vtable->klass->element_class */
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable));
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_ldflda (mb, m_class_offsetof_element_class ());
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_stloc (mb, aklass);
-
- /* vklass = value->vtable->klass */
- mono_mb_emit_ldarg (mb, 2);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable));
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_stloc (mb, vklass);
-
- /* if (vklass->idepth < aklass->idepth) goto failue */
- mono_mb_emit_ldloc (mb, vklass);
- mono_mb_emit_ldflda (mb, m_class_offsetof_idepth ());
- mono_mb_emit_byte (mb, CEE_LDIND_U2);
-
- mono_mb_emit_ldloc (mb, aklass);
- mono_mb_emit_ldflda (mb, m_class_offsetof_idepth ());
- mono_mb_emit_byte (mb, CEE_LDIND_U2);
-
- b2 = mono_mb_emit_branch (mb, CEE_BLT_UN);
-
- /* if (vklass->supertypes [aklass->idepth - 1] != aklass) goto failure */
- mono_mb_emit_ldloc (mb, vklass);
- mono_mb_emit_ldflda (mb, m_class_offsetof_supertypes ());
- mono_mb_emit_byte (mb, CEE_LDIND_I);
-
- mono_mb_emit_ldloc (mb, aklass);
- mono_mb_emit_ldflda (mb, m_class_offsetof_idepth ());
- mono_mb_emit_byte (mb, CEE_LDIND_U2);
- mono_mb_emit_icon (mb, 1);
- mono_mb_emit_byte (mb, CEE_SUB);
- mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P);
- mono_mb_emit_byte (mb, CEE_MUL);
- mono_mb_emit_byte (mb, CEE_ADD);
- mono_mb_emit_byte (mb, CEE_LDIND_I);
-
- mono_mb_emit_ldloc (mb, aklass);
-
- b3 = mono_mb_emit_branch (mb, CEE_BNE_UN);
-
- copy_pos = mono_mb_get_label (mb);
- /* do_store */
- mono_mb_patch_branch (mb, b1);
- mono_mb_emit_ldloc (mb, array_slot_addr);
- mono_mb_emit_ldarg (mb, 2);
- mono_mb_emit_byte (mb, CEE_STIND_REF);
-
- mono_mb_emit_byte (mb, CEE_RET);
-
- /* the hard way */
- mono_mb_patch_branch (mb, b2);
- mono_mb_patch_branch (mb, b3);
-
- mono_mb_emit_ldarg (mb, 2);
- mono_mb_emit_ldloc (mb, aklass);
- mono_mb_emit_icall (mb, mono_object_isinst_icall);
-
- b4 = mono_mb_emit_branch (mb, CEE_BRTRUE);
- mono_mb_patch_addr (mb, b4, copy_pos - (b4 + 4));
- mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
-
- mono_mb_emit_byte (mb, CEE_RET);
-}
+ mono_mb_emit_byte (mb, CEE_LDELEM_REF);
-static void
-mb_emit_byte_ilgen (MonoMethodBuilder *mb, guint8 op)
-{
- mono_mb_emit_byte (mb, op);
-}
+ mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (conv, &stind_op));
+ mono_mb_emit_byte (mb, stind_op);
+ }
+ else {
+ char *msg = g_strdup ("Marshalling of non-string and non-blittable arrays to managed code is not implemented.");
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ return conv_arg;
+ }
-static void
-emit_array_address_ilgen (MonoMethodBuilder *mb, int rank, int elem_size)
-{
- int i, bounds, ind, realidx;
- int branch_pos, *branch_positions;
+ mono_mb_emit_add_to_local (mb, index_var, 1);
+ mono_mb_emit_add_to_local (mb, dest_ptr, esize);
- MonoType *int_type = mono_get_int_type ();
- MonoType *int32_type = mono_get_int32_type ();
-
- branch_positions = g_new0 (int, rank);
-
- bounds = mono_mb_add_local (mb, int_type);
- ind = mono_mb_add_local (mb, int32_type);
- realidx = mono_mb_add_local (mb, int32_type);
-
- /* bounds = array->bounds; */
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoArray, bounds));
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_stloc (mb, bounds);
-
- /* ind is the overall element index, realidx is the partial index in a single dimension */
- /* ind = idx0 - bounds [0].lower_bound */
- mono_mb_emit_ldarg (mb, 1);
- mono_mb_emit_ldloc (mb, bounds);
- mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
- mono_mb_emit_byte (mb, CEE_ADD);
- mono_mb_emit_byte (mb, CEE_LDIND_I4);
- mono_mb_emit_byte (mb, CEE_SUB);
- mono_mb_emit_stloc (mb, ind);
- /* if (ind >= bounds [0].length) goto exeception; */
- mono_mb_emit_ldloc (mb, ind);
- mono_mb_emit_ldloc (mb, bounds);
- mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
- mono_mb_emit_byte (mb, CEE_ADD);
- mono_mb_emit_byte (mb, CEE_LDIND_I4);
- /* note that we use unsigned comparison */
- branch_pos = mono_mb_emit_branch (mb, CEE_BGE_UN);
-
- /* For large ranks (> 4?) use a loop n IL later to reduce code size.
- * We could also decide to ignore the passed elem_size and get it
- * from the array object, to reduce the number of methods we generate:
- * the additional cost is 3 memory loads and a non-immediate mul.
- */
- for (i = 1; i < rank; ++i) {
- /* realidx = idxi - bounds [i].lower_bound */
- mono_mb_emit_ldarg (mb, 1 + i);
- mono_mb_emit_ldloc (mb, bounds);
- mono_mb_emit_icon (mb, (i * sizeof (MonoArrayBounds)) + MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
- mono_mb_emit_byte (mb, CEE_ADD);
- mono_mb_emit_byte (mb, CEE_LDIND_I4);
- mono_mb_emit_byte (mb, CEE_SUB);
- mono_mb_emit_stloc (mb, realidx);
- /* if (realidx >= bounds [i].length) goto exeception; */
- mono_mb_emit_ldloc (mb, realidx);
- mono_mb_emit_ldloc (mb, bounds);
- mono_mb_emit_icon (mb, (i * sizeof (MonoArrayBounds)) + MONO_STRUCT_OFFSET (MonoArrayBounds, length));
- mono_mb_emit_byte (mb, CEE_ADD);
- mono_mb_emit_byte (mb, CEE_LDIND_I4);
- branch_positions [i] = mono_mb_emit_branch (mb, CEE_BGE_UN);
- /* ind = ind * bounds [i].length + realidx */
- mono_mb_emit_ldloc (mb, ind);
- mono_mb_emit_ldloc (mb, bounds);
- mono_mb_emit_icon (mb, (i * sizeof (MonoArrayBounds)) + MONO_STRUCT_OFFSET (MonoArrayBounds, length));
- mono_mb_emit_byte (mb, CEE_ADD);
- mono_mb_emit_byte (mb, CEE_LDIND_I4);
- mono_mb_emit_byte (mb, CEE_MUL);
- mono_mb_emit_ldloc (mb, realidx);
- mono_mb_emit_byte (mb, CEE_ADD);
- mono_mb_emit_stloc (mb, ind);
- }
+ mono_mb_emit_branch_label (mb, CEE_BR, label2);
- /* return array->vector + ind * element_size */
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoArray, vector));
- mono_mb_emit_ldloc (mb, ind);
- if (elem_size) {
- mono_mb_emit_icon (mb, elem_size);
- } else {
- /* Load arr->vtable->klass->sizes.element_class */
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_byte (mb, CEE_CONV_I);
- mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoObject, vtable));
- mono_mb_emit_byte (mb, CEE_ADD);
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
- mono_mb_emit_byte (mb, CEE_ADD);
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- /* sizes is an union, so this reads sizes.element_size */
- mono_mb_emit_icon (mb, m_class_offsetof_sizes ());
- mono_mb_emit_byte (mb, CEE_ADD);
- mono_mb_emit_byte (mb, CEE_LDIND_I4);
- }
- mono_mb_emit_byte (mb, CEE_MUL);
- mono_mb_emit_byte (mb, CEE_ADD);
- mono_mb_emit_byte (mb, CEE_RET);
+ mono_mb_patch_branch (mb, label1);
+ mono_mb_patch_branch (mb, label3);
+#endif
- /* patch the branches to get here and throw */
- for (i = 1; i < rank; ++i) {
- mono_mb_patch_branch (mb, branch_positions [i]);
+ break;
}
- mono_mb_patch_branch (mb, branch_pos);
- /* throw exception */
- mono_mb_emit_exception (mb, "IndexOutOfRangeException", NULL);
-
- g_free (branch_positions);
-}
-
-static void
-emit_delegate_begin_invoke_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig)
-{
- int params_var;
- params_var = mono_mb_emit_save_args (mb, sig, FALSE);
-
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ldloc (mb, params_var);
- mono_mb_emit_icall (mb, mono_delegate_begin_invoke);
- mono_mb_emit_byte (mb, CEE_RET);
-}
-
-static void
-emit_delegate_end_invoke_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig)
-{
- int params_var;
- params_var = mono_mb_emit_save_args (mb, sig, FALSE);
-
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ldloc (mb, params_var);
- mono_mb_emit_icall (mb, mono_delegate_end_invoke);
-
- if (sig->ret->type == MONO_TYPE_VOID) {
- mono_mb_emit_byte (mb, CEE_POP);
- mono_mb_emit_byte (mb, CEE_RET);
- } else
- mono_mb_emit_restore_result (mb, sig->ret);
-}
+ case MARSHAL_ACTION_MANAGED_CONV_RESULT: {
+#ifndef DISABLE_NONBLITTABLE
+ guint32 label1, label2, label3;
+ int index_var, src, dest, esize;
+ MonoMarshalConv conv = MONO_MARSHAL_CONV_INVALID;
+ gboolean is_string = FALSE;
-static void
-emit_delegate_invoke_internal_ilgen (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)
-{
- int local_i, local_len, local_delegates, local_d, local_target, local_res = 0;
- int pos0, pos1, pos2;
- int i;
- gboolean void_ret;
+ g_assert (!m_type_is_byref (t));
- MonoType *int32_type = mono_get_int32_type ();
- MonoType *object_type = mono_get_object_type ();
+ mono_marshal_load_type_info (eklass);
- void_ret = sig->ret->type == MONO_TYPE_VOID && !method->string_ctor;
-
- /* allocate local 0 (object) */
- local_i = mono_mb_add_local (mb, int32_type);
- local_len = mono_mb_add_local (mb, int32_type);
- local_delegates = mono_mb_add_local (mb, m_class_get_byval_arg (mono_defaults.array_class));
- local_d = mono_mb_add_local (mb, m_class_get_byval_arg (mono_defaults.multicastdelegate_class));
- local_target = mono_mb_add_local (mb, object_type);
-
- if (!void_ret)
- local_res = mono_mb_add_local (mb, m_class_get_byval_arg (mono_class_from_mono_type_internal (sig->ret)));
-
- g_assert (sig->hasthis);
-
- /*
- * {type: sig->ret} res;
- * if (delegates == null) {
- * return this.<target> ( args .. );
- * } else {
- * int i = 0, len = this.delegates.Length;
- * do {
- * res = this.delegates [i].Invoke ( args .. );
- * } while (++i < len);
- * return res;
- * }
- */
-
- /* this wrapper can be used in unmanaged-managed transitions */
- emit_thread_interrupt_checkpoint (mb);
-
- /* delegates = this.delegates */
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoMulticastDelegate, delegates));
- mono_mb_emit_byte (mb, CEE_LDIND_REF);
- mono_mb_emit_stloc (mb, local_delegates);
-
- /* if (delegates == null) */
- mono_mb_emit_ldloc (mb, local_delegates);
- pos2 = mono_mb_emit_branch (mb, CEE_BRTRUE);
-
- /* return target.<target_method|method_ptr> ( args .. ); */
-
- /* target = d.target; */
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, target));
- mono_mb_emit_byte (mb, CEE_LDIND_REF);
- mono_mb_emit_stloc (mb, local_target);
-
- /*static methods with bound first arg can have null target and still be bound*/
- if (!static_method_with_first_arg_bound) {
- /* if target != null */
- mono_mb_emit_ldloc (mb, local_target);
- pos0 = mono_mb_emit_branch (mb, CEE_BRFALSE);
-
- /* then call this->method_ptr nonstatic */
- if (callvirt) {
- // FIXME:
- mono_mb_emit_exception_full (mb, "System", "NotImplementedException", "");
- } else {
- mono_mb_emit_ldloc (mb, local_target);
- for (i = 0; i < sig->param_count; ++i)
- mono_mb_emit_ldarg (mb, i + 1);
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, extra_arg));
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_byte (mb, CEE_MONO_LD_DELEGATE_METHOD_PTR);
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_op (mb, CEE_MONO_CALLI_EXTRA_ARG, sig);
- mono_mb_emit_byte (mb, CEE_RET);
+ if (eklass == mono_defaults.string_class) {
+ is_string = TRUE;
+ conv = mono_marshal_get_string_to_ptr_conv (m->piinfo, spec);
+ }
+ else {
+ g_assert_not_reached ();
}
- /* else [target == null] call this->method_ptr static */
- mono_mb_patch_branch (mb, pos0);
- }
+ if (is_string)
+ esize = TARGET_SIZEOF_VOID_P;
+ else if (eklass == mono_defaults.char_class)
+ esize = mono_pinvoke_is_unicode (m->piinfo) ? 2 : 1;
+ else
+ esize = mono_class_native_size (eklass, NULL);
- if (callvirt) {
- if (!closed_over_null) {
- /* if target_method is not really virtual, turn it into a direct call */
- if (!(target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) || m_class_is_valuetype (target_class)) {
- mono_mb_emit_ldarg (mb, 1);
- for (i = 1; i < sig->param_count; ++i)
- mono_mb_emit_ldarg (mb, i + 1);
- mono_mb_emit_op (mb, CEE_CALL, target_method);
- } else {
- mono_mb_emit_ldarg (mb, 1);
- mono_mb_emit_op (mb, CEE_CASTCLASS, target_class);
- for (i = 1; i < sig->param_count; ++i)
- mono_mb_emit_ldarg (mb, i + 1);
- mono_mb_emit_op (mb, CEE_CALLVIRT, target_method);
- }
- } else {
- mono_mb_emit_byte (mb, CEE_LDNULL);
- for (i = 0; i < sig->param_count; ++i)
- mono_mb_emit_ldarg (mb, i + 1);
- mono_mb_emit_op (mb, CEE_CALL, target_method);
- }
- } else {
- if (static_method_with_first_arg_bound) {
- mono_mb_emit_ldloc (mb, local_target);
- if (!MONO_TYPE_IS_REFERENCE (invoke_sig->params[0]))
- mono_mb_emit_op (mb, CEE_UNBOX_ANY, mono_class_from_mono_type_internal (invoke_sig->params[0]));
- }
- for (i = 0; i < sig->param_count; ++i)
- mono_mb_emit_ldarg (mb, i + 1);
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, extra_arg));
- mono_mb_emit_byte (mb, CEE_LDIND_I);
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_byte (mb, CEE_MONO_LD_DELEGATE_METHOD_PTR);
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_op (mb, CEE_MONO_CALLI_EXTRA_ARG, invoke_sig);
- }
+ src = mono_mb_add_local (mb, object_type);
+ dest = mono_mb_add_local (mb, int_type);
- mono_mb_emit_byte (mb, CEE_RET);
+ mono_mb_emit_stloc (mb, src);
+ mono_mb_emit_ldloc (mb, src);
+ mono_mb_emit_stloc (mb, 3);
- /* else [delegates != null] */
- mono_mb_patch_branch (mb, pos2);
+ /* Check for null */
+ mono_mb_emit_ldloc (mb, src);
+ label1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
- /* len = delegates.Length; */
- mono_mb_emit_ldloc (mb, local_delegates);
- mono_mb_emit_byte (mb, CEE_LDLEN);
- mono_mb_emit_byte (mb, CEE_CONV_I4);
- mono_mb_emit_stloc (mb, local_len);
+ /* Allocate native array */
+ mono_mb_emit_icon (mb, esize);
+ mono_mb_emit_ldloc (mb, src);
+ mono_mb_emit_byte (mb, CEE_LDLEN);
- /* i = 0; */
- mono_mb_emit_icon (mb, 0);
- mono_mb_emit_stloc (mb, local_i);
+ if (eklass == mono_defaults.string_class) {
+ /* Make the array bigger for the terminating null */
+ mono_mb_emit_byte (mb, CEE_LDC_I4_1);
+ mono_mb_emit_byte (mb, CEE_ADD);
+ }
+ mono_mb_emit_byte (mb, CEE_MUL);
+ mono_mb_emit_icall (mb, ves_icall_marshal_alloc);
+ mono_mb_emit_stloc (mb, dest);
+ mono_mb_emit_ldloc (mb, dest);
+ mono_mb_emit_stloc (mb, 3);
- pos1 = mono_mb_get_label (mb);
+ /* Emit marshalling loop */
+ index_var = mono_mb_add_local (mb, int_type);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_0);
+ mono_mb_emit_stloc (mb, index_var);
+ label2 = mono_mb_get_label (mb);
+ mono_mb_emit_ldloc (mb, index_var);
+ mono_mb_emit_ldloc (mb, src);
+ mono_mb_emit_byte (mb, CEE_LDLEN);
+ label3 = mono_mb_emit_branch (mb, CEE_BGE);
- /* d = delegates [i]; */
- mono_mb_emit_ldloc (mb, local_delegates);
- mono_mb_emit_ldloc (mb, local_i);
- mono_mb_emit_byte (mb, CEE_LDELEM_REF);
- mono_mb_emit_stloc (mb, local_d);
+ /* Emit marshalling code */
+ if (is_string) {
+ int stind_op;
+ g_assert (conv != MONO_MARSHAL_CONV_INVALID);
- /* res = d.Invoke ( args .. ); */
- mono_mb_emit_ldloc (mb, local_d);
- for (i = 0; i < sig->param_count; i++)
- mono_mb_emit_ldarg (mb, i + 1);
- if (!ctx) {
- mono_mb_emit_op (mb, CEE_CALLVIRT, method);
- } else {
- ERROR_DECL (error);
- mono_mb_emit_op (mb, CEE_CALLVIRT, mono_class_inflate_generic_method_checked (method, &container->context, error));
- g_assert (is_ok (error)); /* FIXME don't swallow the error */
- }
+ /* dest */
+ mono_mb_emit_ldloc (mb, dest);
- if (!void_ret)
- mono_mb_emit_stloc (mb, local_res);
+ /* src */
+ mono_mb_emit_ldloc (mb, src);
+ mono_mb_emit_ldloc (mb, index_var);
- /* i += 1 */
- mono_mb_emit_add_to_local (mb, local_i, 1);
+ mono_mb_emit_byte (mb, CEE_LDELEM_REF);
- /* i < l */
- mono_mb_emit_ldloc (mb, local_i);
- mono_mb_emit_ldloc (mb, local_len);
- mono_mb_emit_branch_label (mb, CEE_BLT, pos1);
+ mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (conv, &stind_op));
+ mono_mb_emit_byte (mb, stind_op);
+ }
+ else {
+ char *msg = g_strdup ("Marshalling of non-string arrays to managed code is not implemented.");
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ return conv_arg;
+ }
- /* return res */
- if (!void_ret)
- mono_mb_emit_ldloc (mb, local_res);
- mono_mb_emit_byte (mb, CEE_RET);
-}
+ mono_mb_emit_add_to_local (mb, index_var, 1);
+ mono_mb_emit_add_to_local (mb, dest, esize);
-static void
-mb_skip_visibility_ilgen (MonoMethodBuilder *mb)
-{
- mb->skip_visibility = 1;
-}
+ mono_mb_emit_branch_label (mb, CEE_BR, label2);
-static void
-mb_set_dynamic_ilgen (MonoMethodBuilder *mb)
-{
- mb->dynamic = 1;
+ mono_mb_patch_branch (mb, label3);
+ mono_mb_patch_branch (mb, label1);
+#endif
+ break;
+ }
+ default:
+ g_assert_not_reached ();
+ }
+ return conv_arg;
}
-static void
-emit_synchronized_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, MonoGenericContext *ctx, MonoGenericContainer *container, MonoMethod *enter_method, MonoMethod *exit_method, MonoMethod *gettypefromhandle_method)
+static gboolean
+emit_native_wrapper_validate_signature (MonoMethodBuilder *mb, MonoMethodSignature* sig, MonoMarshalSpec** mspecs)
{
- int i, pos, pos2, this_local, taken_local, ret_local = 0;
- MonoMethodSignature *sig = mono_method_signature_internal (method);
- MonoExceptionClause *clause;
-
- /* result */
- if (!MONO_TYPE_IS_VOID (sig->ret))
- ret_local = mono_mb_add_local (mb, sig->ret);
-
- if (m_class_is_valuetype (method->klass) && !(method->flags & MONO_METHOD_ATTR_STATIC)) {
- /* FIXME Is this really the best way to signal an error here? Isn't this called much later after class setup? -AK */
- mono_class_set_type_load_failure (method->klass, "");
- /* This will throw the type load exception when the wrapper is compiled */
- mono_mb_emit_byte (mb, CEE_LDNULL);
- mono_mb_emit_op (mb, CEE_ISINST, method->klass);
- mono_mb_emit_byte (mb, CEE_POP);
+ if (mspecs) {
+ for (int i = 0; i < sig->param_count; i ++) {
+ if (mspecs [i + 1] && mspecs [i + 1]->native == MONO_NATIVE_CUSTOM) {
+ if (!mspecs [i + 1]->data.custom_data.custom_name || *mspecs [i + 1]->data.custom_data.custom_name == '\0') {
+ mono_mb_emit_exception_full (mb, "System", "TypeLoadException", g_strdup ("Missing ICustomMarshaler type"));
+ return FALSE;
+ }
- if (!MONO_TYPE_IS_VOID (sig->ret))
- mono_mb_emit_ldloc (mb, ret_local);
- mono_mb_emit_byte (mb, CEE_RET);
+ switch (sig->params[i]->type) {
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_OBJECT:
+ case MONO_TYPE_STRING:
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY:
+ case MONO_TYPE_VALUETYPE:
+ break;
- return;
+ default:
+ mono_mb_emit_exception_full (mb, "System.Runtime.InteropServices", "MarshalDirectiveException", g_strdup_printf ("custom marshalling of type %x is currently not supported", sig->params[i]->type));
+ return FALSE;
+ }
+ }
+ else if (sig->params[i]->type == MONO_TYPE_VALUETYPE) {
+ MonoMarshalType *marshal_type = mono_marshal_load_type_info (mono_class_from_mono_type_internal (sig->params [i]));
+ for (int field_idx = 0; field_idx < marshal_type->num_fields; ++field_idx) {
+ if (marshal_type->fields [field_idx].mspec && marshal_type->fields [field_idx].mspec->native == MONO_NATIVE_CUSTOM) {
+ mono_mb_emit_exception_full (mb, "System", "TypeLoadException", g_strdup ("Value type includes custom marshaled fields"));
+ return FALSE;
+ }
+ }
+ }
+ }
}
- MonoType *object_type = mono_get_object_type ();
- MonoType *boolean_type = m_class_get_byval_arg (mono_defaults.boolean_class);
- /* this */
- this_local = mono_mb_add_local (mb, object_type);
- taken_local = mono_mb_add_local (mb, boolean_type);
-
- clause = (MonoExceptionClause *)mono_image_alloc0 (get_method_image (method), sizeof (MonoExceptionClause));
- clause->flags = MONO_EXCEPTION_CLAUSE_FINALLY;
-
- /* Push this or the type object */
- if (method->flags & METHOD_ATTRIBUTE_STATIC) {
- /* We have special handling for this in the JIT */
- int index = mono_mb_add_data (mb, method->klass);
- mono_mb_add_data (mb, mono_defaults.typehandle_class);
- mono_mb_emit_byte (mb, CEE_LDTOKEN);
- mono_mb_emit_i4 (mb, index);
-
- mono_mb_emit_managed_call (mb, gettypefromhandle_method, NULL);
- }
- else
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_stloc (mb, this_local);
-
- clause->try_offset = mono_mb_get_label (mb);
- /* Call Monitor::Enter() */
- mono_mb_emit_ldloc (mb, this_local);
- mono_mb_emit_ldloc_addr (mb, taken_local);
- mono_mb_emit_managed_call (mb, enter_method, NULL);
-
- /* Call the method */
- if (sig->hasthis)
- mono_mb_emit_ldarg (mb, 0);
- for (i = 0; i < sig->param_count; i++)
- mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
-
- if (ctx) {
- ERROR_DECL (error);
- mono_mb_emit_managed_call (mb, mono_class_inflate_generic_method_checked (method, &container->context, error), NULL);
- g_assert (is_ok (error)); /* FIXME don't swallow the error */
- } else {
- mono_mb_emit_managed_call (mb, method, NULL);
- }
+ return TRUE;
+}
- if (!MONO_TYPE_IS_VOID (sig->ret))
- mono_mb_emit_stloc (mb, ret_local);
+static int
+emit_marshal_ptr_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
+ MonoMarshalSpec *spec, int conv_arg,
+ MonoType **conv_arg_type, MarshalAction action)
+{
+ MonoMethodBuilder *mb = m->mb;
+ switch (action) {
+ case MARSHAL_ACTION_CONV_IN:
+ /* MS seems to allow this in some cases, ie. bxc #158 */
+ /*
+ if (MONO_TYPE_ISSTRUCT (t->data.type) && !mono_class_from_mono_type_internal (t->data.type)->blittable) {
+ char *msg = g_strdup_printf ("Can not marshal 'parameter #%d': Pointers can not reference marshaled structures. Use byref instead.", argnum + 1);
+ mono_marshal_shared_mb_emit_exception_marshal_directive (m->mb, msg);
+ }
+ */
+ break;
- pos = mono_mb_emit_branch (mb, CEE_LEAVE);
+ case MARSHAL_ACTION_PUSH:
+ mono_mb_emit_ldarg (mb, argnum);
+ break;
- clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
- clause->handler_offset = mono_mb_get_label (mb);
+ case MARSHAL_ACTION_CONV_RESULT:
+ /* no conversions necessary */
+ mono_mb_emit_stloc (mb, 3);
+ break;
- /* Call Monitor::Exit() if needed */
- mono_mb_emit_ldloc (mb, taken_local);
- pos2 = mono_mb_emit_branch (mb, CEE_BRFALSE);
- mono_mb_emit_ldloc (mb, this_local);
- mono_mb_emit_managed_call (mb, exit_method, NULL);
- mono_mb_patch_branch (mb, pos2);
- mono_mb_emit_byte (mb, CEE_ENDFINALLY);
+ default:
+ break;
+ }
+ return conv_arg;
+}
- clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
+static int
+emit_marshal_boolean_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
+ MonoMarshalSpec *spec,
+ int conv_arg, MonoType **conv_arg_type,
+ MarshalAction action)
+{
+ MonoMethodBuilder *mb = m->mb;
+ MonoType *int_type = mono_get_int_type ();
+ MonoType *boolean_type = m_class_get_byval_arg (mono_defaults.boolean_class);
- mono_mb_patch_branch (mb, pos);
- if (!MONO_TYPE_IS_VOID (sig->ret))
- mono_mb_emit_ldloc (mb, ret_local);
- mono_mb_emit_byte (mb, CEE_RET);
+ switch (action) {
+ case MARSHAL_ACTION_CONV_IN: {
+ MonoType *local_type;
+ int label_false;
+ guint8 ldc_op = CEE_LDC_I4_1;
- mono_mb_set_clauses (mb, 1, clause);
-}
+ local_type = mono_marshal_boolean_conv_in_get_local_type (spec, &ldc_op);
+ if (m_type_is_byref (t))
+ *conv_arg_type = int_type;
+ else
+ *conv_arg_type = local_type;
+ conv_arg = mono_mb_add_local (mb, local_type);
-static void
-emit_unbox_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method)
-{
- MonoMethodSignature *sig = mono_method_signature_internal (method);
-
- mono_mb_emit_ldarg (mb, 0);
- mono_mb_emit_icon (mb, MONO_ABI_SIZEOF (MonoObject));
- mono_mb_emit_byte (mb, CEE_ADD);
- for (int i = 0; i < sig->param_count; ++i)
- mono_mb_emit_ldarg (mb, i + 1);
- mono_mb_emit_managed_call (mb, method, NULL);
- mono_mb_emit_byte (mb, CEE_RET);
-}
+ mono_mb_emit_ldarg (mb, argnum);
+ if (m_type_is_byref (t))
+ mono_mb_emit_byte (mb, CEE_LDIND_I1);
+ label_false = mono_mb_emit_branch (mb, CEE_BRFALSE);
+ mono_mb_emit_byte (mb, ldc_op);
+ mono_mb_emit_stloc (mb, conv_arg);
+ mono_mb_patch_branch (mb, label_false);
-static void
-emit_array_accessor_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *sig, MonoGenericContext *ctx)
-{
- MonoGenericContainer *container = NULL;
- /* Call the method */
- if (sig->hasthis)
- mono_mb_emit_ldarg (mb, 0);
- for (int i = 0; i < sig->param_count; i++)
- mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
-
- if (ctx) {
- ERROR_DECL (error);
- mono_mb_emit_managed_call (mb, mono_class_inflate_generic_method_checked (method, &container->context, error), NULL);
- g_assert (is_ok (error)); /* FIXME don't swallow the error */
- } else {
- mono_mb_emit_managed_call (mb, method, NULL);
+ break;
}
- mono_mb_emit_byte (mb, CEE_RET);
-}
-static void
-emit_generic_array_helper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *csig)
-{
- mono_mb_emit_ldarg (mb, 0);
- for (int i = 0; i < csig->param_count; i++)
- mono_mb_emit_ldarg (mb, i + 1);
- mono_mb_emit_managed_call (mb, method, NULL);
- mono_mb_emit_byte (mb, CEE_RET);
-}
+ case MARSHAL_ACTION_CONV_OUT:
+ {
+ int label_false, label_end;
+ if (!m_type_is_byref (t))
+ break;
-static void
-emit_thunk_invoke_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *csig)
-{
- MonoImage *image = get_method_image (method);
- MonoMethodSignature *sig = mono_method_signature_internal (method);
- int param_count = sig->param_count + sig->hasthis + 1;
- int pos_leave, coop_gc_var = 0;
- MonoExceptionClause *clause;
- MonoType *object_type = mono_get_object_type ();
-#if defined (TARGET_WASM)
- const gboolean do_blocking_transition = FALSE;
-#else
- const gboolean do_blocking_transition = TRUE;
-#endif
+ mono_mb_emit_ldarg (mb, argnum);
+ mono_mb_emit_ldloc (mb, conv_arg);
- /* local 0 (temp for exception object) */
- mono_mb_add_local (mb, object_type);
+ label_false = mono_mb_emit_branch (mb, CEE_BRFALSE);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_1);
- /* local 1 (temp for result) */
- if (!MONO_TYPE_IS_VOID (sig->ret))
- mono_mb_add_local (mb, sig->ret);
+ label_end = mono_mb_emit_branch (mb, CEE_BR);
+ mono_mb_patch_branch (mb, label_false);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_0);
+ mono_mb_patch_branch (mb, label_end);
- if (do_blocking_transition) {
- /* local 4, the local to be used when calling the suspend funcs */
- coop_gc_var = mono_mb_add_local (mb, mono_get_int_type ());
+ mono_mb_emit_byte (mb, CEE_STIND_I1);
+ break;
}
- /* 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);
+ case MARSHAL_ACTION_PUSH:
+ if (m_type_is_byref (t))
+ mono_mb_emit_ldloc_addr (mb, conv_arg);
+ else if (conv_arg)
+ mono_mb_emit_ldloc (mb, conv_arg);
+ else
+ mono_mb_emit_ldarg (mb, argnum);
+ break;
- if (do_blocking_transition) {
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_byte (mb, CEE_MONO_GET_SP);
- mono_mb_emit_icall (mb, mono_threads_enter_gc_unsafe_region_unbalanced);
- mono_mb_emit_stloc (mb, coop_gc_var);
- }
+ case MARSHAL_ACTION_CONV_RESULT:
+ /* maybe we need to make sure that it fits within 8 bits */
+ mono_mb_emit_stloc (mb, 3);
+ break;
- /* try */
- clause = (MonoExceptionClause *)mono_image_alloc0 (image, sizeof (MonoExceptionClause));
- clause->try_offset = mono_mb_get_label (mb);
+ case MARSHAL_ACTION_MANAGED_CONV_IN: {
+ MonoClass* conv_arg_class = mono_defaults.int32_class;
+ guint8 ldop = CEE_LDIND_I4;
+ int label_null, label_false;
- /* push method's args */
- for (int i = 0; i < param_count - 1; i++) {
- MonoType *type;
- MonoClass *klass;
+ conv_arg_class = mono_marshal_boolean_managed_conv_in_get_conv_arg_class (spec, &ldop);
+ conv_arg = mono_mb_add_local (mb, boolean_type);
- mono_mb_emit_ldarg (mb, i);
+ if (m_type_is_byref (t))
+ *conv_arg_type = m_class_get_this_arg (conv_arg_class);
+ else
+ *conv_arg_type = m_class_get_byval_arg (conv_arg_class);
- /* get the byval type of the param */
- klass = mono_class_from_mono_type_internal (csig->params [i]);
- type = m_class_get_byval_arg (klass);
- /* unbox struct args */
- if (MONO_TYPE_ISSTRUCT (type)) {
- mono_mb_emit_op (mb, CEE_UNBOX, klass);
+ mono_mb_emit_ldarg (mb, argnum);
- /* byref args & and the "this" arg must remain a ptr.
- Otherwise make a copy of the value type */
- if (!(m_type_is_byref (csig->params [i]) || (i == 0 && sig->hasthis)))
- mono_mb_emit_op (mb, CEE_LDOBJ, klass);
+ /* Check null */
+ if (m_type_is_byref (t)) {
+ label_null = mono_mb_emit_branch (mb, CEE_BRFALSE);
+ mono_mb_emit_ldarg (mb, argnum);
+ mono_mb_emit_byte (mb, ldop);
+ } else
+ label_null = 0;
- csig->params [i] = object_type;
- }
+ label_false = mono_mb_emit_branch (mb, CEE_BRFALSE);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_1);
+ mono_mb_emit_stloc (mb, conv_arg);
+ mono_mb_patch_branch (mb, label_false);
+
+ if (m_type_is_byref (t))
+ mono_mb_patch_branch (mb, label_null);
+ break;
}
- /* call */
- if (method->flags & METHOD_ATTRIBUTE_VIRTUAL)
- mono_mb_emit_op (mb, CEE_CALLVIRT, method);
- else
- mono_mb_emit_op (mb, CEE_CALL, method);
+ case MARSHAL_ACTION_MANAGED_CONV_OUT: {
+ guint8 stop = CEE_STIND_I4;
+ guint8 ldc_op = CEE_LDC_I4_1;
+ int label_null,label_false, label_end;
- /* save result at local 1 */
- if (!MONO_TYPE_IS_VOID (sig->ret))
- mono_mb_emit_stloc (mb, 1);
+ if (!m_type_is_byref (t))
+ break;
+ if (spec) {
+ switch (spec->native) {
+ case MONO_NATIVE_I1:
+ case MONO_NATIVE_U1:
+ stop = CEE_STIND_I1;
+ break;
+ case MONO_NATIVE_VARIANTBOOL:
+ stop = CEE_STIND_I2;
+ ldc_op = CEE_LDC_I4_M1;
+ break;
+ default:
+ break;
+ }
+ }
- pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);
+ /* Check null */
+ mono_mb_emit_ldarg (mb, argnum);
+ label_null = mono_mb_emit_branch (mb, CEE_BRFALSE);
- /* 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;
+ mono_mb_emit_ldarg (mb, argnum);
+ mono_mb_emit_ldloc (mb, conv_arg);
- clause->handler_offset = mono_mb_get_label (mb);
+ label_false = mono_mb_emit_branch (mb, CEE_BRFALSE);
+ mono_mb_emit_byte (mb, ldc_op);
+ label_end = mono_mb_emit_branch (mb, CEE_BR);
- /* 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);
+ mono_mb_patch_branch (mb, label_false);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_0);
+ mono_mb_patch_branch (mb, label_end);
- clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
+ mono_mb_emit_byte (mb, stop);
+ mono_mb_patch_branch (mb, label_null);
+ break;
+ }
- mono_mb_set_clauses (mb, 1, clause);
+ default:
+ g_assert_not_reached ();
+ }
+ return conv_arg;
+}
- mono_mb_patch_branch (mb, pos_leave);
- /* end-try */
+static int
+emit_marshal_char_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
+ MonoMarshalSpec *spec, int conv_arg,
+ MonoType **conv_arg_type, MarshalAction action)
+{
+ MonoMethodBuilder *mb = m->mb;
- if (!MONO_TYPE_IS_VOID (sig->ret)) {
- mono_mb_emit_ldloc (mb, 1);
+ switch (action) {
+ case MARSHAL_ACTION_PUSH:
+ /* fixme: dont know how to marshal that. We cant simply
+ * convert it to a one byte UTF8 character, because an
+ * unicode character may need more that one byte in UTF8 */
+ mono_mb_emit_ldarg (mb, argnum);
+ break;
- /* box the return value */
- if (MONO_TYPE_ISSTRUCT (sig->ret))
- mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type_internal (sig->ret));
- }
+ case MARSHAL_ACTION_CONV_RESULT:
+ /* fixme: we need conversions here */
+ mono_mb_emit_stloc (mb, 3);
+ break;
- if (do_blocking_transition) {
- mono_mb_emit_ldloc (mb, coop_gc_var);
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_byte (mb, CEE_MONO_GET_SP);
- mono_mb_emit_icall (mb, mono_threads_exit_gc_unsafe_region_unbalanced);
+ default:
+ break;
}
-
- mono_mb_emit_byte (mb, CEE_RET);
+ return conv_arg;
}
-
static int
emit_marshal_custom_ilgen_throw_exception (MonoMethodBuilder *mb, const char *exc_nspace, const char *exc_name, const char *msg, MarshalAction action)
{
return conv_arg;
}
-
static int
emit_marshal_safehandle_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
MonoMarshalSpec *spec, int conv_arg,
mono_mb_emit_stloc (mb, 0);
/* Set dest */
- mono_mb_emit_ldarg (mb, argnum);
- mono_mb_emit_stloc (mb, 1);
-
- /* emit valuetype conversion code */
- mono_marshal_shared_emit_struct_conv (mb, klass, FALSE);
- }
- break;
-
- case MARSHAL_ACTION_MANAGED_CONV_RESULT:
- if (m_class_is_delegate (klass)) {
- mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (MONO_MARSHAL_CONV_DEL_FTN, NULL));
- mono_mb_emit_stloc (mb, 3);
- break;
- }
-
- /* The class can not have an automatic layout */
- if (mono_class_is_auto_layout (klass)) {
- mono_mb_emit_auto_layout_exception (mb, klass);
- break;
- }
-
- mono_mb_emit_stloc (mb, 0);
- /* Check for null */
- mono_mb_emit_ldloc (mb, 0);
- pos = mono_mb_emit_branch (mb, CEE_BRTRUE);
- mono_mb_emit_byte (mb, CEE_LDNULL);
- mono_mb_emit_stloc (mb, 3);
- pos2 = mono_mb_emit_branch (mb, CEE_BR);
-
- mono_mb_patch_branch (mb, pos);
-
- /* Set src */
- mono_mb_emit_ldloc (mb, 0);
- mono_mb_emit_ldflda (mb, MONO_ABI_SIZEOF (MonoObject));
- mono_mb_emit_stloc (mb, 0);
-
- /* Allocate and set dest */
- mono_mb_emit_icon (mb, mono_class_native_size (klass, NULL));
- mono_mb_emit_byte (mb, CEE_CONV_I);
- mono_mb_emit_icall (mb, ves_icall_marshal_alloc);
- mono_mb_emit_byte (mb, CEE_DUP);
- mono_mb_emit_stloc (mb, 1);
- mono_mb_emit_stloc (mb, 3);
-
- mono_marshal_shared_emit_struct_conv (mb, klass, FALSE);
-
- mono_mb_patch_branch (mb, pos2);
- break;
-
- default:
- g_assert_not_reached ();
- }
- return conv_arg;
-}
-
-static int
-emit_marshal_variant_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
- MonoMarshalSpec *spec,
- int conv_arg, MonoType **conv_arg_type,
- MarshalAction action)
-{
-#ifndef DISABLE_COM
- MonoMethodBuilder *mb = m->mb;
- MonoType *variant_type = m_class_get_byval_arg (mono_class_get_variant_class ());
- MonoType *variant_type_byref = mono_class_get_byref_type (mono_class_get_variant_class ());
- MonoType *object_type = mono_get_object_type ();
-
- switch (action) {
- case MARSHAL_ACTION_CONV_IN: {
- conv_arg = mono_mb_add_local (mb, variant_type);
-
- if (m_type_is_byref (t))
- *conv_arg_type = variant_type_byref;
- else
- *conv_arg_type = variant_type;
-
- if (m_type_is_byref (t) && !(t->attrs & PARAM_ATTRIBUTE_IN) && t->attrs & PARAM_ATTRIBUTE_OUT)
- break;
-
- mono_mb_emit_ldarg (mb, argnum);
- if (m_type_is_byref (t))
- mono_mb_emit_byte(mb, CEE_LDIND_REF);
- mono_mb_emit_ldloc_addr (mb, conv_arg);
- mono_mb_emit_managed_call (mb, mono_get_Marshal_GetNativeVariantForObject (), NULL);
- break;
- }
-
- case MARSHAL_ACTION_CONV_OUT: {
- if (m_type_is_byref (t) && (t->attrs & PARAM_ATTRIBUTE_OUT || !(t->attrs & PARAM_ATTRIBUTE_IN))) {
- mono_mb_emit_ldarg (mb, argnum);
- mono_mb_emit_ldloc_addr (mb, conv_arg);
- mono_mb_emit_managed_call (mb, mono_get_Marshal_GetObjectForNativeVariant (), NULL);
- mono_mb_emit_byte (mb, CEE_STIND_REF);
- }
-
- mono_mb_emit_ldloc_addr (mb, conv_arg);
- mono_mb_emit_managed_call (mb, mono_get_Variant_Clear (), NULL);
- break;
- }
-
- case MARSHAL_ACTION_PUSH:
- if (m_type_is_byref (t))
- mono_mb_emit_ldloc_addr (mb, conv_arg);
- else
- mono_mb_emit_ldloc (mb, conv_arg);
- break;
-
- case MARSHAL_ACTION_CONV_RESULT: {
- char *msg = g_strdup ("Marshalling of VARIANT not supported as a return type.");
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- break;
- }
-
- case MARSHAL_ACTION_MANAGED_CONV_IN: {
- conv_arg = mono_mb_add_local (mb, object_type);
-
- if (m_type_is_byref (t))
- *conv_arg_type = variant_type_byref;
- else
- *conv_arg_type = variant_type;
-
- if (m_type_is_byref (t) && !(t->attrs & PARAM_ATTRIBUTE_IN) && t->attrs & PARAM_ATTRIBUTE_OUT)
- break;
-
- if (m_type_is_byref (t))
- mono_mb_emit_ldarg (mb, argnum);
- else
- mono_mb_emit_ldarg_addr (mb, argnum);
- mono_mb_emit_managed_call (mb, mono_get_Marshal_GetObjectForNativeVariant (), NULL);
- mono_mb_emit_stloc (mb, conv_arg);
- break;
- }
-
- case MARSHAL_ACTION_MANAGED_CONV_OUT: {
- if (m_type_is_byref (t) && (t->attrs & PARAM_ATTRIBUTE_OUT || !(t->attrs & PARAM_ATTRIBUTE_IN))) {
- mono_mb_emit_ldloc (mb, conv_arg);
- mono_mb_emit_ldarg (mb, argnum);
- mono_mb_emit_managed_call (mb, mono_get_Marshal_GetNativeVariantForObject (), NULL);
- }
- break;
- }
-
- case MARSHAL_ACTION_MANAGED_CONV_RESULT: {
- char *msg = g_strdup ("Marshalling of VARIANT not supported as a return type.");
- mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
- break;
- }
-
- default:
- g_assert_not_reached ();
- }
-#endif /* DISABLE_COM */
-
- return conv_arg;
-}
-
-static gboolean
-emit_managed_wrapper_validate_signature (MonoMethodSignature* sig, MonoMarshalSpec** mspecs, MonoError* error)
-{
- if (mspecs) {
- for (int i = 0; i < sig->param_count; i ++) {
- if (mspecs [i + 1] && mspecs [i + 1]->native == MONO_NATIVE_CUSTOM) {
- if (!mspecs [i + 1]->data.custom_data.custom_name || *mspecs [i + 1]->data.custom_data.custom_name == '\0') {
- mono_error_set_generic_error (error, "System", "TypeLoadException", "Missing ICustomMarshaler type");
- return FALSE;
- }
-
- switch (sig->params[i]->type) {
- case MONO_TYPE_OBJECT:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_VALUETYPE:
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_STRING:
- case MONO_TYPE_BOOLEAN:
- break;
- default:
- mono_error_set_generic_error (error, "System.Runtime.InteropServices", "MarshalDirectiveException", "custom marshalling of type %x is currently not supported", sig->params[i]->type);
- return FALSE;
- }
- } else if (sig->params[i]->type == MONO_TYPE_VALUETYPE) {
- MonoClass *klass = mono_class_from_mono_type_internal (sig->params [i]);
- MonoMarshalType *marshal_type = mono_marshal_load_type_info (klass);
- for (int field_idx = 0; field_idx < marshal_type->num_fields; ++field_idx) {
- if (marshal_type->fields [field_idx].mspec && marshal_type->fields [field_idx].mspec->native == MONO_NATIVE_CUSTOM) {
- mono_error_set_type_load_class (error, klass, "Value type includes custom marshaled fields");
- return FALSE;
- }
- }
- }
- }
- }
-
- return TRUE;
-}
-
-static void
-emit_managed_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *invoke_sig, MonoMarshalSpec **mspecs, EmitMarshalContext* m, MonoMethod *method, MonoGCHandle target_handle, MonoError *error)
-{
- MonoMethodSignature *sig, *csig;
- int i, *tmp_locals, orig_domain, attach_cookie;
- gboolean closed = FALSE;
-
- sig = m->sig;
- csig = m->csig;
-
- if (!sig->hasthis && sig->param_count != invoke_sig->param_count) {
- /* Closed delegate */
- g_assert (sig->param_count == invoke_sig->param_count + 1);
- closed = TRUE;
- /* Use a new signature without the first argument */
- sig = mono_metadata_signature_dup (sig);
- memmove (&sig->params [0], &sig->params [1], (sig->param_count - 1) * sizeof (MonoType*));
- sig->param_count --;
- }
-
- if (!emit_managed_wrapper_validate_signature (sig, mspecs, error)) {
- if (closed)
- g_free (sig);
- return;
- }
-
- MonoType *int_type = mono_get_int_type ();
- MonoType *boolean_type = m_class_get_byval_arg (mono_defaults.boolean_class);
- /* allocate local 0 (pointer) src_ptr */
- mono_mb_add_local (mb, int_type);
- /* allocate local 1 (pointer) dst_ptr */
- mono_mb_add_local (mb, int_type);
- /* allocate local 2 (boolean) delete_old */
- mono_mb_add_local (mb, boolean_type);
-
- if (!MONO_TYPE_IS_VOID(sig->ret)) {
- /* allocate local 3 to store the return value */
- mono_mb_add_local (mb, sig->ret);
- }
-
- if (MONO_TYPE_ISSTRUCT (sig->ret))
- m->vtaddr_var = mono_mb_add_local (mb, int_type);
-
- orig_domain = mono_mb_add_local (mb, int_type);
- attach_cookie = mono_mb_add_local (mb, int_type);
-
- /*
- * // does (STARTING|RUNNING|BLOCKING) -> RUNNING + set/switch domain
- * intptr_t attach_cookie;
- * intptr_t orig_domain = mono_threads_attach_coop (domain, &attach_cookie);
- * <interrupt check>
- *
- * ret = method (...);
- * // does RUNNING -> (RUNNING|BLOCKING) + unset/switch domain
- * mono_threads_detach_coop (orig_domain, &attach_cookie);
- *
- * return ret;
- */
-
- mono_mb_emit_icon (mb, 0);
- mono_mb_emit_stloc (mb, 2);
-
- /* orig_domain = mono_threads_attach_coop (domain, &attach_cookie); */
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_byte (mb, CEE_MONO_LDDOMAIN);
- mono_mb_emit_ldloc_addr (mb, attach_cookie);
- /*
- * This icall is special cased in the JIT so it works in native-to-managed wrappers in unattached threads.
- * Keep this in sync with the CEE_JIT_ICALL code in the JIT.
- *
- * Special cased in interpreter, keep in sync.
- */
- mono_mb_emit_icall (mb, mono_threads_attach_coop);
- mono_mb_emit_stloc (mb, orig_domain);
-
- /* <interrupt check> */
- emit_thread_interrupt_checkpoint (mb);
-
- /* we first do all conversions */
- tmp_locals = g_newa (int, sig->param_count);
- for (i = 0; i < sig->param_count; i ++) {
- MonoType *t = sig->params [i];
- MonoMarshalSpec *spec = mspecs [i + 1];
-
- if (spec && spec->native == MONO_NATIVE_CUSTOM) {
- tmp_locals [i] = mono_emit_marshal (m, i, t, mspecs [i + 1], 0, &csig->params [i], MARSHAL_ACTION_MANAGED_CONV_IN);
- } else {
- switch (t->type) {
- case MONO_TYPE_OBJECT:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_VALUETYPE:
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_STRING:
- case MONO_TYPE_BOOLEAN:
- tmp_locals [i] = mono_emit_marshal (m, i, t, mspecs [i + 1], 0, &csig->params [i], MARSHAL_ACTION_MANAGED_CONV_IN);
- break;
- default:
- tmp_locals [i] = 0;
- break;
- }
- }
- }
-
- if (sig->hasthis) {
- if (target_handle) {
- mono_mb_emit_icon8 (mb, (gint64)target_handle);
- mono_mb_emit_byte (mb, CEE_CONV_I);
- mono_mb_emit_icall (mb, mono_gchandle_get_target_internal);
- } else {
- /* fixme: */
- g_assert_not_reached ();
- }
- } else if (closed) {
- mono_mb_emit_icon8 (mb, (gint64)target_handle);
- mono_mb_emit_byte (mb, CEE_CONV_I);
- mono_mb_emit_icall (mb, mono_gchandle_get_target_internal);
- }
-
- for (i = 0; i < sig->param_count; i++) {
- MonoType *t = sig->params [i];
-
- if (tmp_locals [i]) {
- if (m_type_is_byref (t))
- mono_mb_emit_ldloc_addr (mb, tmp_locals [i]);
- else
- mono_mb_emit_ldloc (mb, tmp_locals [i]);
- }
- else
- mono_mb_emit_ldarg (mb, i);
- }
-
- /* ret = method (...) */
- mono_mb_emit_managed_call (mb, method, NULL);
-
- if (MONO_TYPE_ISSTRUCT (sig->ret) && sig->ret->type != MONO_TYPE_GENERICINST) {
- MonoClass *klass = mono_class_from_mono_type_internal (sig->ret);
- mono_class_init_internal (klass);
- if (!(mono_class_is_explicit_layout (klass) || m_class_is_blittable (klass))) {
- /* This is used by get_marshal_cb ()->emit_marshal_vtype (), but it needs to go right before the call */
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_byte (mb, CEE_MONO_VTADDR);
- mono_mb_emit_stloc (mb, m->vtaddr_var);
- }
- }
-
- if (mspecs [0] && mspecs [0]->native == MONO_NATIVE_CUSTOM) {
- mono_emit_marshal (m, 0, sig->ret, mspecs [0], 0, NULL, MARSHAL_ACTION_MANAGED_CONV_RESULT);
- } else if (!m_type_is_byref (sig->ret)) {
- switch (sig->ret->type) {
- case MONO_TYPE_VOID:
- break;
- case MONO_TYPE_BOOLEAN:
- case MONO_TYPE_I1:
- case MONO_TYPE_U1:
- case MONO_TYPE_CHAR:
- case MONO_TYPE_I2:
- case MONO_TYPE_U2:
- case MONO_TYPE_I4:
- case MONO_TYPE_U4:
- case MONO_TYPE_I:
- case MONO_TYPE_U:
- case MONO_TYPE_PTR:
- case MONO_TYPE_R4:
- case MONO_TYPE_R8:
- case MONO_TYPE_I8:
- case MONO_TYPE_U8:
- case MONO_TYPE_OBJECT:
- mono_mb_emit_stloc (mb, 3);
- break;
- case MONO_TYPE_STRING:
- csig->ret = int_type;
- mono_emit_marshal (m, 0, sig->ret, mspecs [0], 0, NULL, MARSHAL_ACTION_MANAGED_CONV_RESULT);
- break;
- case MONO_TYPE_VALUETYPE:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_SZARRAY:
- mono_emit_marshal (m, 0, sig->ret, mspecs [0], 0, NULL, MARSHAL_ACTION_MANAGED_CONV_RESULT);
- break;
- case MONO_TYPE_GENERICINST: {
- mono_mb_emit_byte (mb, CEE_POP);
- break;
- }
- default:
- g_warning ("return type 0x%02x unknown", sig->ret->type);
- g_assert_not_reached ();
- }
- } else {
- mono_mb_emit_stloc (mb, 3);
- }
-
- /* Convert byref arguments back */
- for (i = 0; i < sig->param_count; i ++) {
- MonoType *t = sig->params [i];
- MonoMarshalSpec *spec = mspecs [i + 1];
+ mono_mb_emit_ldarg (mb, argnum);
+ mono_mb_emit_stloc (mb, 1);
- if (spec && spec->native == MONO_NATIVE_CUSTOM) {
- mono_emit_marshal (m, i, t, mspecs [i + 1], tmp_locals [i], NULL, MARSHAL_ACTION_MANAGED_CONV_OUT);
- }
- else if (m_type_is_byref (t)) {
- switch (t->type) {
- case MONO_TYPE_CLASS:
- case MONO_TYPE_VALUETYPE:
- case MONO_TYPE_OBJECT:
- case MONO_TYPE_STRING:
- case MONO_TYPE_BOOLEAN:
- mono_emit_marshal (m, i, t, mspecs [i + 1], tmp_locals [i], NULL, MARSHAL_ACTION_MANAGED_CONV_OUT);
- break;
- default:
- break;
- }
+ /* emit valuetype conversion code */
+ mono_marshal_shared_emit_struct_conv (mb, klass, FALSE);
}
- else if (invoke_sig->params [i]->attrs & PARAM_ATTRIBUTE_OUT) {
- /* The [Out] information is encoded in the delegate signature */
- switch (t->type) {
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_CLASS:
- case MONO_TYPE_VALUETYPE:
- mono_emit_marshal (m, i, invoke_sig->params [i], mspecs [i + 1], tmp_locals [i], NULL, MARSHAL_ACTION_MANAGED_CONV_OUT);
- break;
- default:
- g_assert_not_reached ();
- }
+ break;
+
+ case MARSHAL_ACTION_MANAGED_CONV_RESULT:
+ if (m_class_is_delegate (klass)) {
+ mono_mb_emit_icall_id (mb, mono_marshal_shared_conv_to_icall (MONO_MARSHAL_CONV_DEL_FTN, NULL));
+ mono_mb_emit_stloc (mb, 3);
+ break;
}
- }
- /* mono_threads_detach_coop (orig_domain, &attach_cookie); */
- mono_mb_emit_ldloc (mb, orig_domain);
- mono_mb_emit_ldloc_addr (mb, attach_cookie);
- /* Special cased in interpreter, keep in sync */
- mono_mb_emit_icall (mb, mono_threads_detach_coop);
+ /* The class can not have an automatic layout */
+ if (mono_class_is_auto_layout (klass)) {
+ mono_mb_emit_auto_layout_exception (mb, klass);
+ break;
+ }
- /* return ret; */
- if (m->retobj_var) {
- mono_mb_emit_ldloc (mb, m->retobj_var);
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_op (mb, CEE_MONO_RETOBJ, m->retobj_class);
- }
- else {
- if (!MONO_TYPE_IS_VOID (sig->ret))
- mono_mb_emit_ldloc (mb, 3);
- mono_mb_emit_byte (mb, CEE_RET);
- }
+ mono_mb_emit_stloc (mb, 0);
+ /* Check for null */
+ mono_mb_emit_ldloc (mb, 0);
+ pos = mono_mb_emit_branch (mb, CEE_BRTRUE);
+ mono_mb_emit_byte (mb, CEE_LDNULL);
+ mono_mb_emit_stloc (mb, 3);
+ pos2 = mono_mb_emit_branch (mb, CEE_BR);
- if (closed)
- g_free (sig);
-}
+ mono_mb_patch_branch (mb, pos);
-static void
-emit_struct_to_ptr_ilgen (MonoMethodBuilder *mb, MonoClass *klass)
-{
- MonoType *int_type = mono_get_int_type ();
- MonoType *boolean_type = m_class_get_byval_arg (mono_defaults.boolean_class);
- if (m_class_is_blittable (klass)) {
- mono_mb_emit_byte (mb, CEE_LDARG_1);
- mono_mb_emit_byte (mb, CEE_LDARG_0);
- mono_mb_emit_ldflda (mb, MONO_ABI_SIZEOF (MonoObject));
- mono_mb_emit_icon (mb, mono_class_value_size (klass, NULL));
- mono_mb_emit_byte (mb, CEE_PREFIX1);
- mono_mb_emit_byte (mb, CEE_CPBLK);
- } else {
-
- /* allocate local 0 (pointer) src_ptr */
- mono_mb_add_local (mb, int_type);
- /* allocate local 1 (pointer) dst_ptr */
- mono_mb_add_local (mb, int_type);
- /* allocate local 2 (boolean) delete_old */
- mono_mb_add_local (mb, boolean_type);
- mono_mb_emit_byte (mb, CEE_LDARG_2);
- mono_mb_emit_stloc (mb, 2);
-
- /* initialize src_ptr to point to the start of object data */
- mono_mb_emit_byte (mb, CEE_LDARG_0);
+ /* Set src */
+ mono_mb_emit_ldloc (mb, 0);
mono_mb_emit_ldflda (mb, MONO_ABI_SIZEOF (MonoObject));
mono_mb_emit_stloc (mb, 0);
- /* initialize dst_ptr */
- mono_mb_emit_byte (mb, CEE_LDARG_1);
+ /* Allocate and set dest */
+ mono_mb_emit_icon (mb, mono_class_native_size (klass, NULL));
+ mono_mb_emit_byte (mb, CEE_CONV_I);
+ mono_mb_emit_icall (mb, ves_icall_marshal_alloc);
+ mono_mb_emit_byte (mb, CEE_DUP);
mono_mb_emit_stloc (mb, 1);
+ mono_mb_emit_stloc (mb, 3);
mono_marshal_shared_emit_struct_conv (mb, klass, FALSE);
- }
-
- mono_mb_emit_byte (mb, CEE_RET);
-}
-
-static void
-emit_ptr_to_struct_ilgen (MonoMethodBuilder *mb, MonoClass *klass)
-{
- MonoType *int_type = mono_get_int_type ();
- if (m_class_is_blittable (klass)) {
- mono_mb_emit_byte (mb, CEE_LDARG_1);
- mono_mb_emit_ldflda (mb, MONO_ABI_SIZEOF (MonoObject));
- mono_mb_emit_byte (mb, CEE_LDARG_0);
- mono_mb_emit_icon (mb, mono_class_value_size (klass, NULL));
- mono_mb_emit_byte (mb, CEE_PREFIX1);
- mono_mb_emit_byte (mb, CEE_CPBLK);
- } else {
-
- /* allocate local 0 (pointer) src_ptr */
- mono_mb_add_local (mb, int_type);
- /* allocate local 1 (pointer) dst_ptr */
- mono_mb_add_local (mb, m_class_get_this_arg (klass));
-
- /* initialize src_ptr to point to the start of object data */
- mono_mb_emit_byte (mb, CEE_LDARG_0);
- mono_mb_emit_stloc (mb, 0);
- /* initialize dst_ptr */
- mono_mb_emit_byte (mb, CEE_LDARG_1);
- mono_mb_emit_ldflda (mb, MONO_ABI_SIZEOF (MonoObject));
- mono_mb_emit_stloc (mb, 1);
+ mono_mb_patch_branch (mb, pos2);
+ break;
- mono_marshal_shared_emit_struct_conv (mb, klass, TRUE);
+ default:
+ g_assert_not_reached ();
}
-
- mono_mb_emit_byte (mb, CEE_RET);
+ return conv_arg;
}
-static void
-emit_create_string_hack_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *csig, MonoMethod *res)
+static int
+emit_marshal_variant_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
+ MonoMarshalSpec *spec,
+ int conv_arg, MonoType **conv_arg_type,
+ MarshalAction action)
{
- int i;
+#ifndef DISABLE_COM
+ MonoMethodBuilder *mb = m->mb;
+ MonoType *variant_type = m_class_get_byval_arg (mono_class_get_variant_class ());
+ MonoType *variant_type_byref = mono_class_get_byref_type (mono_class_get_variant_class ());
+ MonoType *object_type = mono_get_object_type ();
- g_assert (!mono_method_signature_internal (res)->hasthis);
- for (i = 1; i <= csig->param_count; i++)
- mono_mb_emit_ldarg (mb, i);
- mono_mb_emit_managed_call (mb, res, NULL);
- mono_mb_emit_byte (mb, CEE_RET);
-}
+ switch (action) {
+ case MARSHAL_ACTION_CONV_IN: {
+ conv_arg = mono_mb_add_local (mb, variant_type);
-/* How the arguments of an icall should be wrapped */
-typedef enum {
- /* Don't wrap at all, pass the argument as is */
- ICALL_HANDLES_WRAP_NONE,
- /* Wrap the argument in an object handle, pass the handle to the icall */
- ICALL_HANDLES_WRAP_OBJ,
- /* Wrap the argument in an object handle, pass the handle to the icall,
- write the value out from the handle when the icall returns */
- ICALL_HANDLES_WRAP_OBJ_INOUT,
- /* Initialized an object handle to null, pass to the icalls,
- write the value out from the handle when the icall returns */
- ICALL_HANDLES_WRAP_OBJ_OUT,
- /* Wrap the argument (a valuetype reference) in a handle to pin its
- enclosing object, but pass the raw reference to the icall. This is
- also how we pass byref generic parameter arguments to generic method
- icalls (e.g. System.Array:GetGenericValue_icall<T>(int idx, T out value)) */
- ICALL_HANDLES_WRAP_VALUETYPE_REF,
-} IcallHandlesWrap;
-
-typedef struct {
- IcallHandlesWrap wrap;
- // If wrap is OBJ_OUT or OBJ_INOUT this is >= 0 and holds the referenced managed object,
- // in case the actual parameter refers to a native frame.
- // Otherwise it is -1.
- int handle;
-} IcallHandlesLocal;
-
-/*
- * Describes how to wrap the given parameter.
- *
- */
-static IcallHandlesWrap
-signature_param_uses_handles (MonoMethodSignature *sig, MonoMethodSignature *generic_sig, int param)
-{
- /* If there is a generic parameter that isn't passed byref, we don't
- * know how to pass it to an icall that expects some arguments to be
- * wrapped in handles: if the actual argument type is a reference type
- * we'd need to wrap it in a handle, otherwise we'd want to pass it as is.
- */
- /* FIXME: We should eventually relax the assertion, below, to
- * allow generic parameters that are constrained to be reference types.
- */
- g_assert (!generic_sig || !mono_type_is_generic_parameter (generic_sig->params [param]));
-
- /* If the parameter in the generic version of the method signature is a
- * byref type variable T&, pass the corresponding argument by pinning
- * the memory and passing the raw pointer to the icall. Note that we
- * do this even if the actual instantiation is a byref reference type
- * like string& since the C code for the icall has to work uniformly
- * for both valuetypes and reference types.
- */
- if (generic_sig && m_type_is_byref (generic_sig->params [param]) &&
- (generic_sig->params [param]->type == MONO_TYPE_VAR || generic_sig->params [param]->type == MONO_TYPE_MVAR))
- return ICALL_HANDLES_WRAP_VALUETYPE_REF;
-
- if (MONO_TYPE_IS_REFERENCE (sig->params [param])) {
- if (mono_signature_param_is_out (sig, param))
- return ICALL_HANDLES_WRAP_OBJ_OUT;
- else if (m_type_is_byref (sig->params [param]))
- return ICALL_HANDLES_WRAP_OBJ_INOUT;
+ if (m_type_is_byref (t))
+ *conv_arg_type = variant_type_byref;
else
- return ICALL_HANDLES_WRAP_OBJ;
- } else if (m_type_is_byref (sig->params [param]))
- return ICALL_HANDLES_WRAP_VALUETYPE_REF;
- else
- return ICALL_HANDLES_WRAP_NONE;
-}
+ *conv_arg_type = variant_type;
-static void
-emit_native_icall_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *csig, gboolean check_exceptions, gboolean aot, MonoMethodPInvoke *piinfo)
-{
- // FIXME:
- MonoMethodSignature *call_sig = csig;
- gboolean uses_handles = FALSE;
- gboolean foreign_icall = FALSE;
- IcallHandlesLocal *handles_locals = NULL;
- MonoMethodSignature *sig = mono_method_signature_internal (method);
- gboolean need_gc_safe = FALSE;
- GCSafeTransitionBuilder gc_safe_transition_builder;
-
- (void) mono_lookup_internal_call_full (method, FALSE, &uses_handles, &foreign_icall);
-
- if (G_UNLIKELY (foreign_icall)) {
- /* FIXME: we only want the transitions for hybrid suspend. Q: What to do about AOT? */
- need_gc_safe = gc_safe_transition_builder_init (&gc_safe_transition_builder, mb, FALSE);
-
- if (need_gc_safe)
- gc_safe_transition_builder_add_locals (&gc_safe_transition_builder);
- }
+ if (m_type_is_byref (t) && !(t->attrs & PARAM_ATTRIBUTE_IN) && t->attrs & PARAM_ATTRIBUTE_OUT)
+ break;
- if (sig->hasthis) {
- /*
- * Add a null check since public icalls can be called with 'call' which
- * does no such check.
- */
- mono_mb_emit_byte (mb, CEE_LDARG_0);
- const int pos = mono_mb_emit_branch (mb, CEE_BRTRUE);
- mono_mb_emit_exception (mb, "NullReferenceException", NULL);
- mono_mb_patch_branch (mb, pos);
+ mono_mb_emit_ldarg (mb, argnum);
+ if (m_type_is_byref (t))
+ mono_mb_emit_byte(mb, CEE_LDIND_REF);
+ mono_mb_emit_ldloc_addr (mb, conv_arg);
+ mono_mb_emit_managed_call (mb, mono_get_Marshal_GetNativeVariantForObject (), NULL);
+ break;
}
- if (uses_handles) {
- MonoMethodSignature *generic_sig = NULL;
-
- if (method->is_inflated) {
- ERROR_DECL (error);
- MonoMethod *generic_method = ((MonoMethodInflated*)method)->declaring;
- generic_sig = mono_method_signature_checked (generic_method, error);
- mono_error_assert_ok (error);
+ case MARSHAL_ACTION_CONV_OUT: {
+ if (m_type_is_byref (t) && (t->attrs & PARAM_ATTRIBUTE_OUT || !(t->attrs & PARAM_ATTRIBUTE_IN))) {
+ mono_mb_emit_ldarg (mb, argnum);
+ mono_mb_emit_ldloc_addr (mb, conv_arg);
+ mono_mb_emit_managed_call (mb, mono_get_Marshal_GetObjectForNativeVariant (), NULL);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
}
- // FIXME: The stuff from mono_metadata_signature_dup_internal_with_padding ()
- call_sig = mono_metadata_signature_alloc (get_method_image (method), csig->param_count);
- call_sig->param_count = csig->param_count;
- call_sig->ret = csig->ret;
- call_sig->pinvoke = csig->pinvoke;
-
- /* TODO support adding wrappers to non-static struct methods */
- g_assert (!sig->hasthis || !m_class_is_valuetype (mono_method_get_class (method)));
-
- handles_locals = g_new0 (IcallHandlesLocal, csig->param_count);
-
- for (int i = 0; i < csig->param_count; ++i) {
- // Determine which args need to be wrapped in handles and adjust icall signature.
- // Here, a handle is a pointer to a volatile local in a managed frame -- which is sufficient and efficient.
- const IcallHandlesWrap w = signature_param_uses_handles (csig, generic_sig, i);
- handles_locals [i].wrap = w;
- int local = -1;
-
- switch (w) {
- case ICALL_HANDLES_WRAP_OBJ:
- case ICALL_HANDLES_WRAP_OBJ_INOUT:
- case ICALL_HANDLES_WRAP_OBJ_OUT:
- call_sig->params [i] = mono_class_get_byref_type (mono_class_from_mono_type_internal (csig->params[i]));
- break;
- case ICALL_HANDLES_WRAP_NONE:
- case ICALL_HANDLES_WRAP_VALUETYPE_REF:
- call_sig->params [i] = csig->params [i];
- break;
- default:
- g_assert_not_reached ();
- }
+ mono_mb_emit_ldloc_addr (mb, conv_arg);
+ mono_mb_emit_managed_call (mb, mono_get_Variant_Clear (), NULL);
+ break;
+ }
- // Add a local var to hold the references for each out arg.
- switch (w) {
- case ICALL_HANDLES_WRAP_OBJ_INOUT:
- case ICALL_HANDLES_WRAP_OBJ_OUT:
- // FIXME better type
- local = mono_mb_add_local (mb, mono_get_object_type ());
+ case MARSHAL_ACTION_PUSH:
+ if (m_type_is_byref (t))
+ mono_mb_emit_ldloc_addr (mb, conv_arg);
+ else
+ mono_mb_emit_ldloc (mb, conv_arg);
+ break;
- if (!mb->volatile_locals) {
- gpointer mem = mono_image_alloc0 (get_method_image (method), mono_bitset_alloc_size (csig->param_count + 1, 0));
- mb->volatile_locals = mono_bitset_mem_new (mem, csig->param_count + 1, 0);
- }
- mono_bitset_set (mb->volatile_locals, local);
- break;
- case ICALL_HANDLES_WRAP_VALUETYPE_REF:
- case ICALL_HANDLES_WRAP_OBJ:
- if (!mb->volatile_args) {
- gpointer mem = mono_image_alloc0 (get_method_image (method), mono_bitset_alloc_size (csig->param_count + 1, 0));
- mb->volatile_args = mono_bitset_mem_new (mem, csig->param_count + 1, 0);
- }
- mono_bitset_set (mb->volatile_args, i);
- break;
- case ICALL_HANDLES_WRAP_NONE:
- break;
- default:
- g_assert_not_reached ();
- }
- handles_locals [i].handle = local;
-
- // Load each argument. References into the managed heap get wrapped in handles.
- // Handles here are just pointers to managed volatile locals.
- switch (w) {
- case ICALL_HANDLES_WRAP_NONE:
- case ICALL_HANDLES_WRAP_VALUETYPE_REF:
- // argI = argI
- mono_mb_emit_ldarg (mb, i);
- break;
- case ICALL_HANDLES_WRAP_OBJ:
- // argI = &argI_raw
- mono_mb_emit_ldarg_addr (mb, i);
- break;
- case ICALL_HANDLES_WRAP_OBJ_INOUT:
- case ICALL_HANDLES_WRAP_OBJ_OUT:
- // If parameter guaranteeably referred to a managed frame,
- // then could just be passthrough and volatile. Since
- // that cannot be guaranteed, use a managed volatile local intermediate.
- // ObjOut:
- // localI = NULL
- // ObjInOut:
- // localI = *argI_raw
- // &localI
- if (w == ICALL_HANDLES_WRAP_OBJ_OUT) {
- mono_mb_emit_byte (mb, CEE_LDNULL);
- } else {
- mono_mb_emit_ldarg (mb, i);
- mono_mb_emit_byte (mb, CEE_LDIND_REF);
- }
- mono_mb_emit_stloc (mb, local);
- mono_mb_emit_ldloc_addr (mb, local);
- break;
- default:
- g_assert_not_reached ();
- }
- }
- } else {
- for (int i = 0; i < csig->param_count; i++)
- mono_mb_emit_ldarg (mb, i);
+ case MARSHAL_ACTION_CONV_RESULT: {
+ char *msg = g_strdup ("Marshalling of VARIANT not supported as a return type.");
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ break;
}
- if (need_gc_safe)
- gc_safe_transition_builder_emit_enter (&gc_safe_transition_builder, &piinfo->method, aot);
+ case MARSHAL_ACTION_MANAGED_CONV_IN: {
+ conv_arg = mono_mb_add_local (mb, object_type);
- if (aot) {
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_op (mb, CEE_MONO_ICALL_ADDR, &piinfo->method);
- mono_mb_emit_calli (mb, call_sig);
- } else {
- g_assert (piinfo->addr);
- mono_mb_emit_native_call (mb, call_sig, piinfo->addr);
+ if (m_type_is_byref (t))
+ *conv_arg_type = variant_type_byref;
+ else
+ *conv_arg_type = variant_type;
+
+ if (m_type_is_byref (t) && !(t->attrs & PARAM_ATTRIBUTE_IN) && t->attrs & PARAM_ATTRIBUTE_OUT)
+ break;
+
+ if (m_type_is_byref (t))
+ mono_mb_emit_ldarg (mb, argnum);
+ else
+ mono_mb_emit_ldarg_addr (mb, argnum);
+ mono_mb_emit_managed_call (mb, mono_get_Marshal_GetObjectForNativeVariant (), NULL);
+ mono_mb_emit_stloc (mb, conv_arg);
+ break;
}
- if (need_gc_safe)
- gc_safe_transition_builder_emit_exit (&gc_safe_transition_builder);
-
- // Copy back ObjOut and ObjInOut from locals through parameters.
- if (mb->volatile_locals) {
- g_assert (handles_locals);
- for (int i = 0; i < csig->param_count; i++) {
- const int local = handles_locals [i].handle;
- if (local >= 0) {
- // *argI_raw = localI
- mono_mb_emit_ldarg (mb, i);
- mono_mb_emit_ldloc (mb, local);
- mono_mb_emit_byte (mb, CEE_STIND_REF);
- }
+ case MARSHAL_ACTION_MANAGED_CONV_OUT: {
+ if (m_type_is_byref (t) && (t->attrs & PARAM_ATTRIBUTE_OUT || !(t->attrs & PARAM_ATTRIBUTE_IN))) {
+ mono_mb_emit_ldloc (mb, conv_arg);
+ mono_mb_emit_ldarg (mb, argnum);
+ mono_mb_emit_managed_call (mb, mono_get_Marshal_GetNativeVariantForObject (), NULL);
}
+ break;
}
- g_free (handles_locals);
- if (need_gc_safe)
- gc_safe_transition_builder_cleanup (&gc_safe_transition_builder);
-
- if (check_exceptions)
- emit_thread_interrupt_checkpoint (mb);
- mono_mb_emit_byte (mb, CEE_RET);
-}
+ case MARSHAL_ACTION_MANAGED_CONV_RESULT: {
+ char *msg = g_strdup ("Marshalling of VARIANT not supported as a return type.");
+ mono_marshal_shared_mb_emit_exception_marshal_directive (mb, msg);
+ break;
+ }
-static void
-mb_emit_exception_ilgen (MonoMethodBuilder *mb, const char *exc_nspace, const char *exc_name, const char *msg)
-{
- mono_mb_emit_exception_full (mb, exc_nspace, exc_name, msg);
-}
+ default:
+ g_assert_not_reached ();
+ }
+#endif /* DISABLE_COM */
-static void
-mb_emit_exception_for_error_ilgen (MonoMethodBuilder *mb, const MonoError *error)
-{
- mono_mb_emit_exception_for_error (mb, (MonoError*)error);
+ return conv_arg;
}
-static void
-emit_marshal_directive_exception_ilgen (EmitMarshalContext *m, int argnum, const char* msg)
+static MonoMarshalIlgenCallbacks *
+get_marshal_cb (void)
{
- char* fullmsg = NULL;
- if (argnum == 0)
- fullmsg = g_strdup_printf("Error marshalling return value: %s", msg);
- else
- fullmsg = g_strdup_printf("Error marshalling parameter #%d: %s", argnum, msg);
-
- mono_marshal_shared_mb_emit_exception_marshal_directive (m->mb, fullmsg);
+ if (G_UNLIKELY (!ilgen_cb_inited)) {
+#ifdef ENABLE_ILGEN
+ mono_marshal_ilgen_init ();
+#else
+ mono_marshal_noilgen_init_heavyweight ();
+#endif
+ }
+ return &ilgen_marshal_cb;
}
-static void
-emit_vtfixup_ftnptr_ilgen (MonoMethodBuilder *mb, MonoMethod *method, int param_count, guint16 type)
+int
+mono_emit_marshal_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
+ MonoMarshalSpec *spec, int conv_arg,
+ MonoType **conv_arg_type, MarshalAction action, MonoMarshalLightweightCallbacks* lightweigth_cb)
{
- for (int i = 0; i < param_count; i++)
- mono_mb_emit_ldarg (mb, i);
+ if (spec && spec->native == MONO_NATIVE_CUSTOM)
+ return get_marshal_cb ()->emit_marshal_custom (m, argnum, t, spec, conv_arg, conv_arg_type, action);
- if (type & VTFIXUP_TYPE_CALL_MOST_DERIVED)
- mono_mb_emit_op (mb, CEE_CALLVIRT, method);
- else
- mono_mb_emit_op (mb, CEE_CALL, method);
- mono_mb_emit_byte (mb, CEE_RET);
-}
+ if (spec && spec->native == MONO_NATIVE_ASANY)
+ return get_marshal_cb ()->emit_marshal_asany (m, argnum, t, spec, conv_arg, conv_arg_type, action);
-static void
-emit_icall_wrapper_ilgen (MonoMethodBuilder *mb, MonoJitICallInfo *callinfo, MonoMethodSignature *csig2, gboolean check_exceptions)
-{
- MonoMethodSignature *const sig = callinfo->sig;
+ switch (t->type) {
+ case MONO_TYPE_VALUETYPE:
+ if (t->data.klass == mono_class_try_get_handleref_class ())
+ return get_marshal_cb ()->emit_marshal_handleref (m, argnum, t, spec, conv_arg, conv_arg_type, action);
- if (sig->hasthis)
- mono_mb_emit_byte (mb, CEE_LDARG_0);
+ return get_marshal_cb ()->emit_marshal_vtype (m, argnum, t, spec, conv_arg, conv_arg_type, action);
+ case MONO_TYPE_STRING:
+ return get_marshal_cb ()->emit_marshal_string (m, argnum, t, spec, conv_arg, conv_arg_type, action);
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_OBJECT:
+#if !defined(DISABLE_COM)
+ if (spec && spec->native == MONO_NATIVE_STRUCT)
+ return get_marshal_cb ()->emit_marshal_variant (m, argnum, t, spec, conv_arg, conv_arg_type, action);
+#endif
- for (int i = 0; i < sig->param_count; i++)
- mono_mb_emit_ldarg (mb, i + sig->hasthis);
+#if !defined(DISABLE_COM)
+ if ((spec && (spec->native == MONO_NATIVE_IUNKNOWN ||
+ spec->native == MONO_NATIVE_IDISPATCH ||
+ spec->native == MONO_NATIVE_INTERFACE)) ||
+ (t->type == MONO_TYPE_CLASS && mono_cominterop_is_interface(t->data.klass)))
+ return mono_cominterop_emit_marshal_com_interface (m, argnum, t, spec, conv_arg, conv_arg_type, action);
+ if (spec && (spec->native == MONO_NATIVE_SAFEARRAY) &&
+ (spec->data.safearray_data.elem_type == MONO_VARIANT_VARIANT) &&
+ ((action == MARSHAL_ACTION_CONV_OUT) || (action == MARSHAL_ACTION_CONV_IN) || (action == MARSHAL_ACTION_PUSH)))
+ return mono_cominterop_emit_marshal_safearray (m, argnum, t, spec, conv_arg, conv_arg_type, action);
+#endif
- mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
- mono_mb_emit_byte (mb, CEE_MONO_JIT_ICALL_ADDR);
- mono_mb_emit_i4 (mb, mono_jit_icall_info_index (callinfo));
- mono_mb_emit_calli (mb, csig2);
- if (check_exceptions)
- emit_thread_interrupt_checkpoint (mb);
- mono_mb_emit_byte (mb, CEE_RET);
-}
+ if (mono_class_try_get_safehandle_class () != NULL && t->data.klass &&
+ mono_class_is_subclass_of_internal (t->data.klass, mono_class_try_get_safehandle_class (), FALSE))
+ return get_marshal_cb ()->emit_marshal_safehandle (m, argnum, t, spec, conv_arg, conv_arg_type, action);
-static void
-emit_return_ilgen (MonoMethodBuilder *mb)
-{
- mono_mb_emit_byte (mb, CEE_RET);
+ return get_marshal_cb ()->emit_marshal_object (m, argnum, t, spec, conv_arg, conv_arg_type, action);
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY:
+ return get_marshal_cb ()->emit_marshal_array (m, argnum, t, spec, conv_arg, conv_arg_type, action);
+ case MONO_TYPE_BOOLEAN:
+ return get_marshal_cb ()->emit_marshal_boolean (m, argnum, t, spec, conv_arg, conv_arg_type, action);
+ case MONO_TYPE_PTR:
+ return get_marshal_cb ()->emit_marshal_ptr (m, argnum, t, spec, conv_arg, conv_arg_type, action);
+ case MONO_TYPE_CHAR:
+ return get_marshal_cb ()->emit_marshal_char (m, argnum, t, spec, conv_arg, conv_arg_type, action);
+ case MONO_TYPE_I1:
+ case MONO_TYPE_U1:
+ case MONO_TYPE_I2:
+ case MONO_TYPE_U2:
+ case MONO_TYPE_I4:
+ case MONO_TYPE_U4:
+ case MONO_TYPE_I:
+ case MONO_TYPE_U:
+ case MONO_TYPE_R4:
+ case MONO_TYPE_R8:
+ case MONO_TYPE_I8:
+ case MONO_TYPE_U8:
+ case MONO_TYPE_FNPTR:
+ return lightweigth_cb->emit_marshal_scalar (m, argnum, t, spec, conv_arg, conv_arg_type, action);
+ case MONO_TYPE_GENERICINST:
+ if (mono_type_generic_inst_is_valuetype (t))
+ return get_marshal_cb ()->emit_marshal_vtype (m, argnum, t, spec, conv_arg, conv_arg_type, action);
+ else
+ return get_marshal_cb ()->emit_marshal_object (m, argnum, t, spec, conv_arg, conv_arg_type, action);
+ default:
+ return conv_arg;
+ }
}
void
mono_marshal_ilgen_init (void)
{
- MonoMarshalCallbacks cb;
+ MonoMarshalIlgenCallbacks cb;
cb.version = MONO_MARSHAL_CALLBACKS_VERSION;
cb.emit_marshal_array = emit_marshal_array_ilgen;
cb.emit_marshal_ptr = emit_marshal_ptr_ilgen;
- cb.emit_marshal_scalar = emit_marshal_scalar_ilgen;
-#ifndef DISABLE_NONBLITTABLE
- cb.emit_marshal_boolean = emit_marshal_boolean_ilgen;
cb.emit_marshal_char = emit_marshal_char_ilgen;
- cb.emit_marshal_custom = emit_marshal_custom_ilgen;
- cb.emit_marshal_asany = emit_marshal_asany_ilgen;
cb.emit_marshal_vtype = emit_marshal_vtype_ilgen;
cb.emit_marshal_string = emit_marshal_string_ilgen;
+ cb.emit_marshal_variant = emit_marshal_variant_ilgen;
cb.emit_marshal_safehandle = emit_marshal_safehandle_ilgen;
- cb.emit_marshal_handleref = emit_marshal_handleref_ilgen;
cb.emit_marshal_object = emit_marshal_object_ilgen;
- cb.emit_marshal_variant = emit_marshal_variant_ilgen;
-#endif
- cb.emit_castclass = emit_castclass_ilgen;
- cb.emit_struct_to_ptr = emit_struct_to_ptr_ilgen;
- cb.emit_ptr_to_struct = emit_ptr_to_struct_ilgen;
- cb.emit_isinst = emit_isinst_ilgen;
- cb.emit_virtual_stelemref = emit_virtual_stelemref_ilgen;
- cb.emit_stelemref = emit_stelemref_ilgen;
- cb.emit_array_address = emit_array_address_ilgen;
- cb.emit_native_wrapper = emit_native_wrapper_ilgen;
- cb.emit_managed_wrapper = emit_managed_wrapper_ilgen;
- cb.emit_runtime_invoke_body = emit_runtime_invoke_body_ilgen;
- cb.emit_runtime_invoke_dynamic = emit_runtime_invoke_dynamic_ilgen;
- cb.emit_delegate_begin_invoke = emit_delegate_begin_invoke_ilgen;
- cb.emit_delegate_end_invoke = emit_delegate_end_invoke_ilgen;
- cb.emit_delegate_invoke_internal = emit_delegate_invoke_internal_ilgen;
- cb.emit_synchronized_wrapper = emit_synchronized_wrapper_ilgen;
- cb.emit_unbox_wrapper = emit_unbox_wrapper_ilgen;
- cb.emit_array_accessor_wrapper = emit_array_accessor_wrapper_ilgen;
- cb.emit_generic_array_helper = emit_generic_array_helper_ilgen;
- cb.emit_thunk_invoke_wrapper = emit_thunk_invoke_wrapper_ilgen;
- cb.emit_create_string_hack = emit_create_string_hack_ilgen;
- cb.emit_native_icall_wrapper = emit_native_icall_wrapper_ilgen;
- cb.emit_icall_wrapper = emit_icall_wrapper_ilgen;
- cb.emit_return = emit_return_ilgen;
- cb.emit_vtfixup_ftnptr = emit_vtfixup_ftnptr_ilgen;
- cb.mb_skip_visibility = mb_skip_visibility_ilgen;
- cb.mb_set_dynamic = mb_set_dynamic_ilgen;
- cb.mb_emit_exception = mb_emit_exception_ilgen;
- cb.mb_emit_exception_for_error = mb_emit_exception_for_error_ilgen;
- cb.mb_emit_byte = mb_emit_byte_ilgen;
- cb.emit_marshal_directive_exception = emit_marshal_directive_exception_ilgen;
+ cb.emit_marshal_boolean = emit_marshal_boolean_ilgen;
+ cb.emit_marshal_custom = emit_marshal_custom_ilgen;
+ cb.emit_marshal_asany = emit_marshal_asany_ilgen;
+ cb.emit_marshal_handleref = emit_marshal_handleref_ilgen;
+
#ifdef DISABLE_NONBLITTABLE
mono_marshal_noilgen_init_blittable (&cb);
#endif
- mono_install_marshal_callbacks (&cb);
+ mono_install_marshal_callbacks_ilgen (&cb);
}
+
+
--- /dev/null
+/**
+ * \file
+ * Copyright 2018 Microsoft
+ * 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 "metadata/method-builder-ilgen.h"
+#include "metadata/method-builder-ilgen-internals.h"
+#include <mono/metadata/object.h>
+#include <mono/metadata/loader.h>
+#include "cil-coff.h"
+#include "metadata/marshal.h"
+#include "metadata/marshal-internals.h"
+#include "metadata/marshal-ilgen.h"
+#include "metadata/marshal-lightweight.h"
+#include "metadata/marshal-shared.h"
+#include "metadata/tabledefs.h"
+#include <mono/metadata/exception.h>
+#include <mono/metadata/appdomain.h>
+#include "mono/metadata/abi-details.h"
+#include "mono/metadata/class-abi-details.h"
+#include "mono/metadata/class-init.h"
+#include "mono/metadata/debug-helpers.h"
+#include "mono/metadata/threads.h"
+#include "mono/metadata/monitor.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/reflection-internals.h"
+#include "mono/metadata/handle.h"
+#include "mono/metadata/custom-attrs-internals.h"
+#include "mono/metadata/icall-internals.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>
+#include "icall-decl.h"
+
+#define OPDEF(a,b,c,d,e,f,g,h,i,j) \
+ a = i,
+
+enum {
+#include "mono/cil/opcode.def"
+ LAST = 0xff
+};
+#undef OPDEF
+
+static GENERATE_GET_CLASS_WITH_CACHE (date_time, "System", "DateTime");
+static GENERATE_TRY_GET_CLASS_WITH_CACHE (icustom_marshaler, "System.Runtime.InteropServices", "ICustomMarshaler");
+
+static MonoImage*
+get_method_image (MonoMethod *method)
+{
+ return m_class_get_image (method->klass);
+}
+
+/**
+ * mono_mb_strdup:
+ * \param mb the MethodBuilder
+ * \param s a string
+ *
+ * Creates a copy of the string \p s that can be referenced from the IL of \c mb.
+ *
+ * \returns a pointer to the new string which is owned by the method builder
+ */
+char*
+mono_mb_strdup (MonoMethodBuilder *mb, const char *s)
+{
+ char *res;
+ if (!mb->dynamic)
+ res = mono_image_strdup (get_method_image (mb->method), s);
+ else
+ res = g_strdup (s);
+ return res;
+}
+
+#ifndef DISABLE_COM
+
+// FIXME There are multiple caches of "Clear".
+G_GNUC_UNUSED
+static MonoMethod*
+mono_get_Variant_Clear (void)
+{
+ MONO_STATIC_POINTER_INIT (MonoMethod, variant_clear)
+ variant_clear = mono_marshal_shared_get_method_nofail (mono_class_get_variant_class (), "Clear", 0, 0);
+ MONO_STATIC_POINTER_INIT_END (MonoMethod, variant_clear)
+
+ g_assert (variant_clear);
+ return variant_clear;
+}
+
+#endif
+
+// FIXME There are multiple caches of "GetObjectForNativeVariant".
+G_GNUC_UNUSED
+static MonoMethod*
+mono_get_Marshal_GetObjectForNativeVariant (void)
+{
+ MONO_STATIC_POINTER_INIT (MonoMethod, get_object_for_native_variant)
+ get_object_for_native_variant = mono_marshal_shared_get_method_nofail (mono_defaults.marshal_class, "GetObjectForNativeVariant", 1, 0);
+ MONO_STATIC_POINTER_INIT_END (MonoMethod, get_object_for_native_variant)
+
+ g_assert (get_object_for_native_variant);
+ return get_object_for_native_variant;
+}
+
+// FIXME There are multiple caches of "GetNativeVariantForObject".
+G_GNUC_UNUSED
+static MonoMethod*
+mono_get_Marshal_GetNativeVariantForObject (void)
+{
+ MONO_STATIC_POINTER_INIT (MonoMethod, get_native_variant_for_object)
+ get_native_variant_for_object = mono_marshal_shared_get_method_nofail (mono_defaults.marshal_class, "GetNativeVariantForObject", 2, 0);
+ MONO_STATIC_POINTER_INIT_END (MonoMethod, get_native_variant_for_object)
+
+ g_assert (get_native_variant_for_object);
+ return get_native_variant_for_object;
+}
+
+static void
+emit_struct_free (MonoMethodBuilder *mb, MonoClass *klass, int struct_var)
+{
+ /* Call DestroyStructure */
+ /* FIXME: Only do this if needed */
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_op (mb, CEE_MONO_CLASSCONST, klass);
+ mono_mb_emit_ldloc (mb, struct_var);
+ mono_mb_emit_icall (mb, mono_struct_delete_old);
+}
+
+static void
+emit_thread_interrupt_checkpoint (MonoMethodBuilder *mb)
+{
+ // FIXME Put a boolean in MonoMethodBuilder instead.
+ if (strstr (mb->name, "mono_thread_interruption_checkpoint"))
+ return;
+
+ mono_marshal_shared_emit_thread_interrupt_checkpoint_call (mb, MONO_JIT_ICALL_mono_thread_interruption_checkpoint);
+}
+
+static void
+emit_thread_force_interrupt_checkpoint (MonoMethodBuilder *mb)
+{
+ mono_marshal_shared_emit_thread_interrupt_checkpoint_call (mb, MONO_JIT_ICALL_mono_thread_force_interruption_checkpoint_noraise);
+}
+
+void
+mono_marshal_emit_thread_interrupt_checkpoint (MonoMethodBuilder *mb)
+{
+ emit_thread_interrupt_checkpoint (mb);
+}
+
+void
+mono_marshal_emit_thread_force_interrupt_checkpoint (MonoMethodBuilder *mb)
+{
+ emit_thread_force_interrupt_checkpoint (mb);
+}
+
+int
+mono_mb_emit_save_args (MonoMethodBuilder *mb, MonoMethodSignature *sig, gboolean save_this)
+{
+ int i, params_var, tmp_var;
+
+ MonoType *int_type = mono_get_int_type ();
+ /* allocate local (pointer) *params[] */
+ params_var = mono_mb_add_local (mb, int_type);
+ /* allocate local (pointer) tmp */
+ tmp_var = mono_mb_add_local (mb, int_type);
+
+ /* alloate space on stack to store an array of pointers to the arguments */
+ mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P * (sig->param_count + 1));
+ mono_mb_emit_byte (mb, CEE_PREFIX1);
+ mono_mb_emit_byte (mb, CEE_LOCALLOC);
+ mono_mb_emit_stloc (mb, params_var);
+
+ /* tmp = params */
+ mono_mb_emit_ldloc (mb, params_var);
+ mono_mb_emit_stloc (mb, tmp_var);
+
+ if (save_this && sig->hasthis) {
+ mono_mb_emit_ldloc (mb, tmp_var);
+ mono_mb_emit_ldarg_addr (mb, 0);
+ mono_mb_emit_byte (mb, CEE_STIND_I);
+ /* tmp = tmp + sizeof (gpointer) */
+ if (sig->param_count)
+ mono_mb_emit_add_to_local (mb, tmp_var, TARGET_SIZEOF_VOID_P);
+
+ }
+
+ for (i = 0; i < sig->param_count; i++) {
+ mono_mb_emit_ldloc (mb, tmp_var);
+ mono_mb_emit_ldarg_addr (mb, i + sig->hasthis);
+ mono_mb_emit_byte (mb, CEE_STIND_I);
+ /* tmp = tmp + sizeof (gpointer) */
+ if (i < (sig->param_count - 1))
+ mono_mb_emit_add_to_local (mb, tmp_var, TARGET_SIZEOF_VOID_P);
+ }
+
+ return params_var;
+}
+
+
+void
+mono_mb_emit_restore_result (MonoMethodBuilder *mb, MonoType *return_type)
+{
+ MonoType *t = mono_type_get_underlying_type (return_type);
+ MonoType *int_type = mono_get_int_type ();
+
+ if (m_type_is_byref (return_type))
+ return_type = int_type;
+
+ switch (t->type) {
+ case MONO_TYPE_VOID:
+ g_assert_not_reached ();
+ break;
+ case MONO_TYPE_PTR:
+ case MONO_TYPE_FNPTR:
+ case MONO_TYPE_STRING:
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_OBJECT:
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY:
+ /* nothing to do */
+ break;
+ 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:
+ mono_mb_emit_op (mb, CEE_UNBOX, mono_class_from_mono_type_internal (return_type));
+ mono_mb_emit_byte (mb, mono_type_to_ldind (return_type));
+ break;
+ case MONO_TYPE_GENERICINST:
+ if (!mono_type_generic_inst_is_valuetype (t))
+ break;
+ /* fall through */
+ case MONO_TYPE_VALUETYPE: {
+ MonoClass *klass = mono_class_from_mono_type_internal (return_type);
+ mono_mb_emit_op (mb, CEE_UNBOX, klass);
+ mono_mb_emit_op (mb, CEE_LDOBJ, klass);
+ break;
+ }
+ case MONO_TYPE_VAR:
+ case MONO_TYPE_MVAR: {
+ MonoClass *klass = mono_class_from_mono_type_internal (return_type);
+ mono_mb_emit_op (mb, CEE_UNBOX_ANY, klass);
+ break;
+ }
+ default:
+ g_warning ("type 0x%x not handled", return_type->type);
+ g_assert_not_reached ();
+ }
+
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+/*
+ * emit_invoke_call:
+ *
+ * Emit the call to the wrapper method from a runtime invoke wrapper.
+ */
+static void
+emit_invoke_call (MonoMethodBuilder *mb, MonoMethod *method,
+ MonoMethodSignature *sig, MonoMethodSignature *callsig,
+ int loc_res,
+ gboolean virtual_, gboolean need_direct_wrapper)
+{
+ int i;
+ int *tmp_nullable_locals;
+ gboolean void_ret = FALSE;
+ gboolean string_ctor = method && method->string_ctor;
+
+ if (virtual_) {
+ g_assert (sig->hasthis);
+ g_assert (method->flags & METHOD_ATTRIBUTE_VIRTUAL);
+ }
+
+ if (sig->hasthis) {
+ if (string_ctor) {
+ /* This will call the code emitted by mono_marshal_get_native_wrapper () which ignores it */
+ mono_mb_emit_icon (mb, 0);
+ mono_mb_emit_byte (mb, CEE_CONV_I);
+ } else {
+ mono_mb_emit_ldarg (mb, 0);
+ }
+ }
+
+ tmp_nullable_locals = g_new0 (int, sig->param_count);
+
+ for (i = 0; i < sig->param_count; i++) {
+ MonoType *t = sig->params [i];
+ int type;
+
+ mono_mb_emit_ldarg (mb, 1);
+ if (i) {
+ mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P * i);
+ mono_mb_emit_byte (mb, CEE_ADD);
+ }
+
+ if (m_type_is_byref (t)) {
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ /* A Nullable<T> type don't have a boxed form, it's either null or a boxed T.
+ * So to make this work we unbox it to a local variablee and push a reference to that.
+ */
+ if (t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type_internal (t))) {
+ tmp_nullable_locals [i] = mono_mb_add_local (mb, m_class_get_byval_arg (mono_class_from_mono_type_internal (t)));
+
+ mono_mb_emit_op (mb, CEE_UNBOX_ANY, mono_class_from_mono_type_internal (t));
+ mono_mb_emit_stloc (mb, tmp_nullable_locals [i]);
+ mono_mb_emit_ldloc_addr (mb, tmp_nullable_locals [i]);
+ }
+ continue;
+ }
+
+ type = sig->params [i]->type;
+handle_enum:
+ switch (type) {
+ case MONO_TYPE_I1:
+ case MONO_TYPE_BOOLEAN:
+ case MONO_TYPE_U1:
+ case MONO_TYPE_I2:
+ case MONO_TYPE_U2:
+ case MONO_TYPE_CHAR:
+ case MONO_TYPE_I:
+ case MONO_TYPE_U:
+ case MONO_TYPE_I4:
+ case MONO_TYPE_U4:
+ case MONO_TYPE_R4:
+ case MONO_TYPE_R8:
+ case MONO_TYPE_I8:
+ case MONO_TYPE_U8:
+ mono_mb_emit_no_nullcheck (mb);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_no_nullcheck (mb);
+ mono_mb_emit_byte (mb, mono_type_to_ldind (sig->params [i]));
+ break;
+ case MONO_TYPE_STRING:
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_PTR:
+ case MONO_TYPE_FNPTR:
+ case MONO_TYPE_SZARRAY:
+ case MONO_TYPE_OBJECT:
+ mono_mb_emit_no_nullcheck (mb);
+ mono_mb_emit_byte (mb, mono_type_to_ldind (sig->params [i]));
+ break;
+ case MONO_TYPE_GENERICINST:
+ if (!mono_type_generic_inst_is_valuetype (sig->params [i])) {
+ mono_mb_emit_no_nullcheck (mb);
+ mono_mb_emit_byte (mb, mono_type_to_ldind (sig->params [i]));
+ break;
+ }
+
+ t = m_class_get_byval_arg (t->data.generic_class->container_class);
+ type = t->type;
+ goto handle_enum;
+ case MONO_TYPE_VALUETYPE:
+ if (type == MONO_TYPE_VALUETYPE && m_class_is_enumtype (t->data.klass)) {
+ type = mono_class_enum_basetype_internal (t->data.klass)->type;
+ goto handle_enum;
+ }
+ mono_mb_emit_no_nullcheck (mb);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ if (mono_class_is_nullable (mono_class_from_mono_type_internal (sig->params [i]))) {
+ /* Need to convert a boxed vtype to an mp to a Nullable struct */
+ mono_mb_emit_op (mb, CEE_UNBOX, mono_class_from_mono_type_internal (sig->params [i]));
+ mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type_internal (sig->params [i]));
+ } else {
+ mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type_internal (sig->params [i]));
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ if (virtual_) {
+ mono_mb_emit_op (mb, CEE_CALLVIRT, method);
+ } else if (need_direct_wrapper) {
+ mono_mb_emit_op (mb, CEE_CALL, method);
+ } else {
+ mono_mb_emit_ldarg (mb, 3);
+ mono_mb_emit_calli (mb, callsig);
+ }
+
+ if (m_type_is_byref (sig->ret)) {
+ /* perform indirect load and return by value */
+ int pos;
+ mono_mb_emit_byte (mb, CEE_DUP);
+ pos = mono_mb_emit_branch (mb, CEE_BRTRUE);
+ mono_mb_emit_exception_full (mb, "Mono", "NullByRefReturnException", NULL);
+ mono_mb_patch_branch (mb, pos);
+
+ int ldind_op;
+ MonoType* ret_byval = m_class_get_byval_arg (mono_class_from_mono_type_internal (sig->ret));
+ g_assert (!m_type_is_byref (ret_byval));
+ // TODO: Handle null references
+ ldind_op = mono_type_to_ldind (ret_byval);
+ /* taken from similar code in mini-generic-sharing.c
+ * we need to use mono_mb_emit_op to add method data when loading
+ * a structure since method-to-ir needs this data for wrapper methods */
+ if (ldind_op == CEE_LDOBJ)
+ mono_mb_emit_op (mb, CEE_LDOBJ, mono_class_from_mono_type_internal (ret_byval));
+ else
+ mono_mb_emit_byte (mb, ldind_op);
+ }
+
+ switch (sig->ret->type) {
+ case MONO_TYPE_VOID:
+ if (!string_ctor)
+ void_ret = TRUE;
+ break;
+ case MONO_TYPE_BOOLEAN:
+ case MONO_TYPE_CHAR:
+ case MONO_TYPE_I1:
+ case MONO_TYPE_U1:
+ case MONO_TYPE_I2:
+ case MONO_TYPE_U2:
+ case MONO_TYPE_I4:
+ case MONO_TYPE_U4:
+ case MONO_TYPE_I:
+ case MONO_TYPE_U:
+ case MONO_TYPE_R4:
+ case MONO_TYPE_R8:
+ case MONO_TYPE_I8:
+ case MONO_TYPE_U8:
+ case MONO_TYPE_VALUETYPE:
+ case MONO_TYPE_TYPEDBYREF:
+ case MONO_TYPE_GENERICINST:
+ /* box value types */
+ mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type_internal (sig->ret));
+ break;
+ case MONO_TYPE_STRING:
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY:
+ case MONO_TYPE_OBJECT:
+ /* nothing to do */
+ break;
+ case MONO_TYPE_PTR:
+ /* The result is an IntPtr */
+ mono_mb_emit_op (mb, CEE_BOX, mono_defaults.int_class);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (!void_ret)
+ mono_mb_emit_stloc (mb, loc_res);
+
+ /* Convert back nullable-byref arguments */
+ for (i = 0; i < sig->param_count; i++) {
+ MonoType *t = sig->params [i];
+
+ /*
+ * Box the result and put it back into the array, the caller will have
+ * to obtain it from there.
+ */
+ if (m_type_is_byref (t) && t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type_internal (t))) {
+ mono_mb_emit_ldarg (mb, 1);
+ mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P * i);
+ mono_mb_emit_byte (mb, CEE_ADD);
+
+ mono_mb_emit_ldloc (mb, tmp_nullable_locals [i]);
+ mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type_internal (t));
+
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+ }
+ }
+
+ g_free (tmp_nullable_locals);
+}
+
+static void
+emit_runtime_invoke_body_ilgen (MonoMethodBuilder *mb, const char **param_names, MonoImage *image, MonoMethod *method,
+ MonoMethodSignature *sig, MonoMethodSignature *callsig,
+ gboolean virtual_, gboolean need_direct_wrapper)
+{
+ gint32 labels [16];
+ MonoExceptionClause *clause;
+ int loc_res, loc_exc;
+
+ mono_mb_set_param_names (mb, param_names);
+
+ /* The wrapper looks like this:
+ *
+ * <interrupt check>
+ * if (exc) {
+ * try {
+ * return <call>
+ * } catch (Exception e) {
+ * *exc = e;
+ * }
+ * } else {
+ * return <call>
+ * }
+ */
+
+ MonoType *object_type = mono_get_object_type ();
+ /* allocate local 0 (object) tmp */
+ loc_res = mono_mb_add_local (mb, object_type);
+ /* allocate local 1 (object) exc */
+ loc_exc = mono_mb_add_local (mb, object_type);
+
+ /* *exc is assumed to be initialized to NULL by the caller */
+
+ mono_mb_emit_byte (mb, CEE_LDARG_2);
+ labels [0] = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /*
+ * if (exc) case
+ */
+ labels [1] = mono_mb_get_label (mb);
+ emit_thread_force_interrupt_checkpoint (mb);
+ emit_invoke_call (mb, method, sig, callsig, loc_res, virtual_, need_direct_wrapper);
+
+ labels [2] = mono_mb_emit_branch (mb, CEE_LEAVE);
+
+ /* Add a try clause around the call */
+ clause = (MonoExceptionClause *)mono_image_alloc0 (image, sizeof (MonoExceptionClause));
+ clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
+ clause->data.catch_class = mono_defaults.exception_class;
+ clause->try_offset = labels [1];
+ clause->try_len = mono_mb_get_label (mb) - labels [1];
+
+ clause->handler_offset = mono_mb_get_label (mb);
+
+ /* handler code */
+ mono_mb_emit_stloc (mb, loc_exc);
+ mono_mb_emit_byte (mb, CEE_LDARG_2);
+ mono_mb_emit_ldloc (mb, loc_exc);
+ 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_set_clauses (mb, 1, clause);
+
+ mono_mb_patch_branch (mb, labels [2]);
+ mono_mb_emit_ldloc (mb, loc_res);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /*
+ * if (!exc) case
+ */
+ mono_mb_patch_branch (mb, labels [0]);
+ emit_thread_force_interrupt_checkpoint (mb);
+ emit_invoke_call (mb, method, sig, callsig, loc_res, virtual_, need_direct_wrapper);
+
+ mono_mb_emit_ldloc (mb, 0);
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+static void
+emit_runtime_invoke_dynamic_ilgen (MonoMethodBuilder *mb)
+{
+ int pos;
+ MonoExceptionClause *clause;
+
+ MonoType *object_type = mono_get_object_type ();
+ /* allocate local 0 (object) tmp */
+ mono_mb_add_local (mb, object_type);
+ /* allocate local 1 (object) exc */
+ mono_mb_add_local (mb, object_type);
+
+ /* cond set *exc to null */
+ mono_mb_emit_byte (mb, CEE_LDARG_1);
+ mono_mb_emit_byte (mb, CEE_BRFALSE_S);
+ mono_mb_emit_byte (mb, 3);
+ mono_mb_emit_byte (mb, CEE_LDARG_1);
+ mono_mb_emit_byte (mb, CEE_LDNULL);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+
+ emit_thread_force_interrupt_checkpoint (mb);
+
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+ mono_mb_emit_byte (mb, CEE_LDARG_2);
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_DYN_CALL);
+
+ pos = mono_mb_emit_branch (mb, CEE_LEAVE);
+
+ clause = (MonoExceptionClause *)mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
+ clause->flags = MONO_EXCEPTION_CLAUSE_FILTER;
+ clause->try_len = mono_mb_get_label (mb);
+
+ /* filter code */
+ clause->data.filter_offset = mono_mb_get_label (mb);
+
+ mono_mb_emit_byte (mb, CEE_POP);
+ mono_mb_emit_byte (mb, CEE_LDARG_1);
+ mono_mb_emit_byte (mb, CEE_LDC_I4_0);
+ mono_mb_emit_byte (mb, CEE_PREFIX1);
+ mono_mb_emit_byte (mb, CEE_CGT_UN);
+ mono_mb_emit_byte (mb, CEE_PREFIX1);
+ mono_mb_emit_byte (mb, CEE_ENDFILTER);
+
+ clause->handler_offset = mono_mb_get_label (mb);
+
+ /* handler code */
+ /* store exception */
+ mono_mb_emit_stloc (mb, 1);
+
+ mono_mb_emit_byte (mb, CEE_LDARG_1);
+ mono_mb_emit_ldloc (mb, 1);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+
+ mono_mb_emit_byte (mb, CEE_LDNULL);
+ mono_mb_emit_stloc (mb, 0);
+
+ mono_mb_emit_branch (mb, CEE_LEAVE);
+
+ clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
+
+ mono_mb_set_clauses (mb, 1, clause);
+
+ /* return result */
+ mono_mb_patch_branch (mb, pos);
+ //mono_mb_emit_ldloc (mb, 0);
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+typedef struct EmitGCSafeTransitionBuilder {
+ MonoMethodBuilder *mb;
+ gboolean func_param;
+ int coop_gc_var;
+#ifndef DISABLE_COM
+ int coop_cominterop_fnptr;
+#endif
+} GCSafeTransitionBuilder;
+
+static gboolean
+gc_safe_transition_builder_init (GCSafeTransitionBuilder *builder, MonoMethodBuilder *mb, gboolean func_param)
+{
+ builder->mb = mb;
+ builder->func_param = func_param;
+ builder->coop_gc_var = -1;
+#ifndef DISABLE_COM
+ builder->coop_cominterop_fnptr = -1;
+#endif
+#if defined (TARGET_WASM)
+ return FALSE;
+#else
+ return TRUE;
+#endif
+}
+
+/**
+ * adds locals for the gc safe transition to the method builder.
+ */
+static void
+gc_safe_transition_builder_add_locals (GCSafeTransitionBuilder *builder)
+{
+ MonoType *int_type = mono_get_int_type();
+ /* local 4, the local to be used when calling the suspend funcs */
+ builder->coop_gc_var = mono_mb_add_local (builder->mb, int_type);
+#ifndef DISABLE_COM
+ if (!builder->func_param && MONO_CLASS_IS_IMPORT (builder->mb->method->klass)) {
+ builder->coop_cominterop_fnptr = mono_mb_add_local (builder->mb, int_type);
+ }
+#endif
+}
+
+/**
+ * emits
+ * cookie = mono_threads_enter_gc_safe_region_unbalanced (ref dummy);
+ *
+ */
+static void
+gc_safe_transition_builder_emit_enter (GCSafeTransitionBuilder *builder, MonoMethod *method, gboolean aot)
+{
+
+ // Perform an extra, early lookup of the function address, so any exceptions
+ // potentially resulting from the lookup occur before entering blocking mode.
+ if (!builder->func_param && !MONO_CLASS_IS_IMPORT (builder->mb->method->klass) && aot) {
+ mono_mb_emit_byte (builder->mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_op (builder->mb, CEE_MONO_ICALL_ADDR, method);
+ mono_mb_emit_byte (builder->mb, CEE_POP); // Result not needed yet
+ }
+
+#ifndef DISABLE_COM
+ if (!builder->func_param && MONO_CLASS_IS_IMPORT (builder->mb->method->klass)) {
+ mono_mb_emit_cominterop_get_function_pointer (builder->mb, method);
+ mono_mb_emit_stloc (builder->mb, builder->coop_cominterop_fnptr);
+ }
+#endif
+
+ mono_mb_emit_byte (builder->mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (builder->mb, CEE_MONO_GET_SP);
+ mono_mb_emit_icall (builder->mb, mono_threads_enter_gc_safe_region_unbalanced);
+ mono_mb_emit_stloc (builder->mb, builder->coop_gc_var);
+}
+
+/**
+ * emits
+ * mono_threads_exit_gc_safe_region_unbalanced (cookie, ref dummy);
+ *
+ */
+static void
+gc_safe_transition_builder_emit_exit (GCSafeTransitionBuilder *builder)
+{
+ mono_mb_emit_ldloc (builder->mb, builder->coop_gc_var);
+ mono_mb_emit_byte (builder->mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (builder->mb, CEE_MONO_GET_SP);
+ mono_mb_emit_icall (builder->mb, mono_threads_exit_gc_safe_region_unbalanced);
+}
+
+static void
+gc_safe_transition_builder_cleanup (GCSafeTransitionBuilder *builder)
+{
+ builder->mb = NULL;
+ builder->coop_gc_var = -1;
+#ifndef DISABLE_COM
+ builder->coop_cominterop_fnptr = -1;
+#endif
+}
+
+static gboolean
+emit_native_wrapper_validate_signature (MonoMethodBuilder *mb, MonoMethodSignature* sig, MonoMarshalSpec** mspecs)
+{
+ if (mspecs) {
+ for (int i = 0; i < sig->param_count; i ++) {
+ if (mspecs [i + 1] && mspecs [i + 1]->native == MONO_NATIVE_CUSTOM) {
+ if (!mspecs [i + 1]->data.custom_data.custom_name || strlen (mspecs [i + 1]->data.custom_data.custom_name) == 0) {
+ mono_mb_emit_exception_full (mb, "System", "TypeLoadException", g_strdup ("Missing ICustomMarshaler type"));
+ return FALSE;
+ }
+
+ switch (sig->params[i]->type) {
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_OBJECT:
+ case MONO_TYPE_STRING:
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY:
+ case MONO_TYPE_VALUETYPE:
+ break;
+
+ default:
+ mono_mb_emit_exception_full (mb, "System.Runtime.InteropServices", "MarshalDirectiveException", g_strdup_printf ("custom marshalling of type %x is currently not supported", sig->params[i]->type));
+ return FALSE;
+ }
+ }
+ else if (sig->params[i]->type == MONO_TYPE_VALUETYPE) {
+ MonoMarshalType *marshal_type = mono_marshal_load_type_info (mono_class_from_mono_type_internal (sig->params [i]));
+ for (int field_idx = 0; field_idx < marshal_type->num_fields; ++field_idx) {
+ if (marshal_type->fields [field_idx].mspec && marshal_type->fields [field_idx].mspec->native == MONO_NATIVE_CUSTOM) {
+ mono_mb_emit_exception_full (mb, "System", "TypeLoadException", g_strdup ("Value type includes custom marshaled fields"));
+ return FALSE;
+ }
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ * emit_native_wrapper_ilgen:
+ * \param image the image to use for looking up custom marshallers
+ * \param sig The signature of the native function
+ * \param piinfo Marshalling information
+ * \param mspecs Marshalling information
+ * \param aot whenever the created method will be compiled by the AOT compiler
+ * \param method if non-NULL, the pinvoke method to call
+ * \param check_exceptions Whenever to check for pending exceptions after the native call
+ * \param func_param the function to call is passed as a boxed IntPtr as the first parameter
+ * \param func_param_unboxed combined with \p func_param, expect the function to call as an unboxed IntPtr as the first parameter
+ * \param skip_gc_trans Whenever to skip GC transitions
+ *
+ * generates IL code for the pinvoke wrapper, the generated code calls \p func .
+ */
+static void
+emit_native_wrapper_ilgen (MonoImage *image, MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodPInvoke *piinfo, MonoMarshalSpec **mspecs, gpointer func, MonoNativeWrapperFlags flags)
+{
+ gboolean aot = (flags & EMIT_NATIVE_WRAPPER_AOT) != 0;
+ gboolean check_exceptions = (flags & EMIT_NATIVE_WRAPPER_CHECK_EXCEPTIONS) != 0;
+ gboolean func_param = (flags & EMIT_NATIVE_WRAPPER_FUNC_PARAM) != 0;
+ gboolean func_param_unboxed = (flags & EMIT_NATIVE_WRAPPER_FUNC_PARAM_UNBOXED) != 0;
+ gboolean skip_gc_trans = (flags & EMIT_NATIVE_WRAPPER_SKIP_GC_TRANS) != 0;
+ gboolean runtime_marshalling_enabled = (flags & EMIT_NATIVE_WRAPPER_RUNTIME_MARSHALLING_ENABLED) != 0;
+ EmitMarshalContext m;
+ MonoMethodSignature *csig;
+ MonoClass *klass;
+ int i, argnum, *tmp_locals;
+ int type, param_shift = 0;
+ int func_addr_local = -1;
+ gboolean need_gc_safe = FALSE;
+ GCSafeTransitionBuilder gc_safe_transition_builder;
+
+ memset (&m, 0, sizeof (m));
+ m.runtime_marshalling_enabled = runtime_marshalling_enabled;
+ m.mb = mb;
+ m.sig = sig;
+ m.piinfo = piinfo;
+
+ if (!emit_native_wrapper_validate_signature (mb, sig, mspecs))
+ return;
+
+ if (!skip_gc_trans)
+ need_gc_safe = gc_safe_transition_builder_init (&gc_safe_transition_builder, mb, func_param);
+
+ /* we copy the signature, so that we can set pinvoke to 0 */
+ if (func_param) {
+ /* The function address is passed as the first argument */
+ g_assert (!sig->hasthis);
+ param_shift += 1;
+ }
+ csig = mono_metadata_signature_dup_full (get_method_image (mb->method), sig);
+ csig->pinvoke = 1;
+ if (!runtime_marshalling_enabled)
+ csig->marshalling_disabled = 1;
+ m.csig = csig;
+ m.image = image;
+
+ if (sig->hasthis)
+ param_shift += 1;
+
+ MonoType *int_type = mono_get_int_type ();
+ MonoType *boolean_type = m_class_get_byval_arg (mono_defaults.boolean_class);
+ /* we allocate local for use with mono_marshal_shared_emit_struct_conv() */
+ /* allocate local 0 (pointer) src_ptr */
+ mono_mb_add_local (mb, int_type);
+ /* allocate local 1 (pointer) dst_ptr */
+ mono_mb_add_local (mb, int_type);
+ /* allocate local 2 (boolean) delete_old */
+ mono_mb_add_local (mb, boolean_type);
+
+ /* delete_old = FALSE */
+ mono_mb_emit_icon (mb, 0);
+ mono_mb_emit_stloc (mb, 2);
+
+ if (!MONO_TYPE_IS_VOID (sig->ret)) {
+ /* allocate local 3 to store the return value */
+ mono_mb_add_local (mb, sig->ret);
+ }
+
+ if (need_gc_safe)
+ gc_safe_transition_builder_add_locals (&gc_safe_transition_builder);
+
+ if (!func && !aot && !func_param && !MONO_CLASS_IS_IMPORT (mb->method->klass)) {
+ /*
+ * On netcore, its possible to register pinvoke resolvers at runtime, so
+ * a pinvoke lookup can fail, and then succeed later. So if the
+ * original lookup failed, do a lookup every time until it
+ * succeeds.
+ * This adds some overhead, but only when the pinvoke lookup
+ * was not initially successful.
+ * FIXME: AOT case
+ */
+ func_addr_local = mono_mb_add_local (mb, int_type);
+
+ int cache_local = mono_mb_add_local (mb, int_type);
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_op (mb, CEE_MONO_PINVOKE_ADDR_CACHE, &piinfo->method);
+ mono_mb_emit_stloc (mb, cache_local);
+
+ mono_mb_emit_ldloc (mb, cache_local);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ int pos = mono_mb_emit_branch (mb, CEE_BRTRUE);
+
+ mono_mb_emit_ldloc (mb, cache_local);
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_op (mb, CEE_MONO_METHODCONST, &piinfo->method);
+ mono_mb_emit_icall (mb, mono_marshal_lookup_pinvoke);
+ mono_mb_emit_byte (mb, CEE_STIND_I);
+
+ mono_mb_patch_branch (mb, pos);
+ mono_mb_emit_ldloc (mb, cache_local);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, func_addr_local);
+ }
+
+ /*
+ * cookie = mono_threads_enter_gc_safe_region_unbalanced (ref dummy);
+ *
+ * ret = method (...);
+ *
+ * mono_threads_exit_gc_safe_region_unbalanced (cookie, ref dummy);
+ *
+ * <interrupt check>
+ *
+ * return ret;
+ */
+
+ if (MONO_TYPE_ISSTRUCT (sig->ret))
+ m.vtaddr_var = mono_mb_add_local (mb, int_type);
+
+ if (mspecs [0] && mspecs [0]->native == MONO_NATIVE_CUSTOM) {
+ /* Return type custom marshaling */
+ /*
+ * Since we can't determine the return type of the unmanaged function,
+ * we assume it returns a pointer, and pass that pointer to
+ * MarshalNativeToManaged.
+ */
+ csig->ret = int_type;
+ }
+
+ // Check if SetLastError usage is valid early so we don't try to throw an exception after transitioning GC modes.
+ if (piinfo && (piinfo->piflags & PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR) && !m.runtime_marshalling_enabled)
+ mono_marshal_shared_mb_emit_exception_marshal_directive(mb, g_strdup("Setting SetLastError=true is not supported when runtime marshalling is disabled."));
+
+ /* we first do all conversions */
+ tmp_locals = g_newa (int, sig->param_count);
+ m.orig_conv_args = g_newa (int, sig->param_count + 1);
+
+ for (i = 0; i < sig->param_count; i ++) {
+ tmp_locals [i] = mono_emit_marshal (&m, i + param_shift, sig->params [i], mspecs [i + 1], 0, &csig->params [i], MARSHAL_ACTION_CONV_IN);
+ }
+
+ // In coop mode need to register blocking state during native call
+ if (need_gc_safe)
+ gc_safe_transition_builder_emit_enter (&gc_safe_transition_builder, &piinfo->method, aot);
+
+ /* push all arguments */
+
+ if (sig->hasthis)
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+
+ for (i = 0; i < sig->param_count; i++) {
+ mono_emit_marshal (&m, i + param_shift, sig->params [i], mspecs [i + 1], tmp_locals [i], NULL, MARSHAL_ACTION_PUSH);
+ }
+
+ /* call the native method */
+ if (func_param) {
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+ if (!func_param_unboxed) {
+ mono_mb_emit_op (mb, CEE_UNBOX, mono_defaults.int_class);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ }
+ if (piinfo && (piinfo->piflags & PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR) != 0) {
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_SAVE_LAST_ERROR);
+ }
+ mono_mb_emit_calli (mb, csig);
+ } else if (MONO_CLASS_IS_IMPORT (mb->method->klass)) {
+#ifndef DISABLE_COM
+ mono_mb_emit_ldloc (mb, gc_safe_transition_builder.coop_cominterop_fnptr);
+ if (piinfo->piflags & PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR) {
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_SAVE_LAST_ERROR);
+ }
+ mono_mb_emit_cominterop_call_function_pointer (mb, csig);
+#else
+ g_assert_not_reached ();
+#endif
+ } else {
+ if (func_addr_local != -1) {
+ mono_mb_emit_ldloc (mb, func_addr_local);
+ } else {
+ if (aot) {
+ /* Reuse the ICALL_ADDR opcode for pinvokes too */
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_op (mb, CEE_MONO_ICALL_ADDR, &piinfo->method);
+ }
+ }
+ if (piinfo->piflags & PINVOKE_ATTRIBUTE_SUPPORTS_LAST_ERROR) {
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_SAVE_LAST_ERROR);
+ }
+ if (func_addr_local != -1 || aot)
+ mono_mb_emit_calli (mb, csig);
+ else
+ mono_mb_emit_native_call (mb, csig, func);
+ }
+
+ if (MONO_TYPE_ISSTRUCT (sig->ret)) {
+ klass = mono_class_from_mono_type_internal (sig->ret);
+ mono_class_init_internal (klass);
+ if (!(mono_class_is_explicit_layout (klass) || m_class_is_blittable (klass))) {
+ /* This is used by emit_marshal_vtype (), but it needs to go right before the call */
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_VTADDR);
+ mono_mb_emit_stloc (mb, m.vtaddr_var);
+ }
+ }
+
+ /* Unblock before converting the result, since that can involve calls into the runtime */
+ if (need_gc_safe)
+ gc_safe_transition_builder_emit_exit (&gc_safe_transition_builder);
+
+ gc_safe_transition_builder_cleanup (&gc_safe_transition_builder);
+
+ /* convert the result */
+ if (!m_type_is_byref (sig->ret)) {
+ MonoMarshalSpec *spec = mspecs [0];
+ type = sig->ret->type;
+
+ if (spec && spec->native == MONO_NATIVE_CUSTOM) {
+ mono_emit_marshal (&m, 0, sig->ret, spec, 0, NULL, MARSHAL_ACTION_CONV_RESULT);
+ } else {
+ handle_enum:
+ switch (type) {
+ case MONO_TYPE_VOID:
+ break;
+ case MONO_TYPE_VALUETYPE:
+ klass = sig->ret->data.klass;
+ if (m_class_is_enumtype (klass)) {
+ type = mono_class_enum_basetype_internal (sig->ret->data.klass)->type;
+ goto handle_enum;
+ }
+ mono_emit_marshal (&m, 0, sig->ret, spec, 0, NULL, MARSHAL_ACTION_CONV_RESULT);
+ break;
+ case MONO_TYPE_I1:
+ case MONO_TYPE_U1:
+ case MONO_TYPE_I2:
+ case MONO_TYPE_U2:
+ case MONO_TYPE_I4:
+ case MONO_TYPE_U4:
+ case MONO_TYPE_I:
+ case MONO_TYPE_U:
+ case MONO_TYPE_R4:
+ case MONO_TYPE_R8:
+ case MONO_TYPE_I8:
+ case MONO_TYPE_U8:
+ case MONO_TYPE_FNPTR:
+ case MONO_TYPE_STRING:
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_OBJECT:
+ case MONO_TYPE_BOOLEAN:
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY:
+ case MONO_TYPE_CHAR:
+ case MONO_TYPE_PTR:
+ case MONO_TYPE_GENERICINST:
+ mono_emit_marshal (&m, 0, sig->ret, spec, 0, NULL, MARSHAL_ACTION_CONV_RESULT);
+ break;
+ case MONO_TYPE_TYPEDBYREF:
+ default:
+ g_warning ("return type 0x%02x unknown", sig->ret->type);
+ g_assert_not_reached ();
+ }
+ }
+ } else {
+ mono_mb_emit_stloc (mb, 3);
+ }
+
+ /*
+ * Need to call this after converting the result since MONO_VTADDR needs
+ * to be adjacent to the call instruction.
+ */
+ if (check_exceptions)
+ emit_thread_interrupt_checkpoint (mb);
+
+ /* we need to convert byref arguments back and free string arrays */
+ for (i = 0; i < sig->param_count; i++) {
+ MonoType *t = sig->params [i];
+ MonoMarshalSpec *spec = mspecs [i + 1];
+
+ argnum = i + param_shift;
+
+ if (spec && ((spec->native == MONO_NATIVE_CUSTOM) || (spec->native == MONO_NATIVE_ASANY))) {
+ mono_emit_marshal (&m, argnum, t, spec, tmp_locals [i], NULL, MARSHAL_ACTION_CONV_OUT);
+ continue;
+ }
+
+ switch (t->type) {
+ case MONO_TYPE_STRING:
+ case MONO_TYPE_VALUETYPE:
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_OBJECT:
+ case MONO_TYPE_SZARRAY:
+ case MONO_TYPE_BOOLEAN:
+ mono_emit_marshal (&m, argnum, t, spec, tmp_locals [i], NULL, MARSHAL_ACTION_CONV_OUT);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!MONO_TYPE_IS_VOID(sig->ret))
+ mono_mb_emit_ldloc (mb, 3);
+
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+/*
+ * The code directly following this is the cache hit, value positive branch
+ *
+ * This function takes a new method builder with 0 locals and adds two locals
+ * to create multiple out-branches and the fall through state of having the object
+ * on the stack after a cache miss
+ */
+static void
+generate_check_cache (int obj_arg_position, int class_arg_position, int cache_arg_position, // In-parameters
+ int *null_obj, int *cache_hit_neg, int *cache_hit_pos, // Out-parameters
+ MonoMethodBuilder *mb)
+{
+ int cache_miss_pos;
+
+ MonoType *int_type = mono_get_int_type ();
+ /* allocate local 0 (pointer) obj_vtable */
+ mono_mb_add_local (mb, int_type);
+ /* allocate local 1 (pointer) cached_vtable */
+ mono_mb_add_local (mb, int_type);
+
+ /*if (!obj)*/
+ mono_mb_emit_ldarg (mb, obj_arg_position);
+ *null_obj = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /*obj_vtable = obj->vtable;*/
+ mono_mb_emit_ldarg (mb, obj_arg_position);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, 0);
+
+ /* cached_vtable = *cache*/
+ mono_mb_emit_ldarg (mb, cache_arg_position);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, 1);
+
+ mono_mb_emit_ldloc (mb, 1);
+ mono_mb_emit_byte (mb, CEE_LDC_I4);
+ mono_mb_emit_i4 (mb, ~0x1);
+ mono_mb_emit_byte (mb, CEE_CONV_I);
+ mono_mb_emit_byte (mb, CEE_AND);
+ mono_mb_emit_ldloc (mb, 0);
+ /*if ((cached_vtable & ~0x1)== obj_vtable)*/
+ cache_miss_pos = mono_mb_emit_branch (mb, CEE_BNE_UN);
+
+ /*return (cached_vtable & 0x1) ? NULL : obj;*/
+ mono_mb_emit_ldloc (mb, 1);
+ mono_mb_emit_byte(mb, CEE_LDC_I4_1);
+ mono_mb_emit_byte (mb, CEE_CONV_U);
+ mono_mb_emit_byte (mb, CEE_AND);
+ *cache_hit_neg = mono_mb_emit_branch (mb, CEE_BRTRUE);
+ *cache_hit_pos = mono_mb_emit_branch (mb, CEE_BR);
+
+ // slow path
+ mono_mb_patch_branch (mb, cache_miss_pos);
+
+ // if isinst
+ mono_mb_emit_ldarg (mb, obj_arg_position);
+ mono_mb_emit_ldarg (mb, class_arg_position);
+ mono_mb_emit_ldarg (mb, cache_arg_position);
+ mono_mb_emit_icall (mb, mono_marshal_isinst_with_cache);
+}
+
+static void
+emit_castclass_ilgen (MonoMethodBuilder *mb)
+{
+ int return_null_pos, positive_cache_hit_pos, negative_cache_hit_pos, invalid_cast_pos;
+ const int obj_arg_position = TYPECHECK_OBJECT_ARG_POS;
+ const int class_arg_position = TYPECHECK_CLASS_ARG_POS;
+ const int cache_arg_position = TYPECHECK_CACHE_ARG_POS;
+
+ generate_check_cache (obj_arg_position, class_arg_position, cache_arg_position,
+ &return_null_pos, &negative_cache_hit_pos, &positive_cache_hit_pos, mb);
+ invalid_cast_pos = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /*return obj;*/
+ mono_mb_patch_branch (mb, positive_cache_hit_pos);
+ mono_mb_emit_ldarg (mb, obj_arg_position);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /*fails*/
+ mono_mb_patch_branch (mb, negative_cache_hit_pos);
+ mono_mb_patch_branch (mb, invalid_cast_pos);
+ mono_mb_emit_exception (mb, "InvalidCastException", NULL);
+
+ /*return null*/
+ mono_mb_patch_branch (mb, return_null_pos);
+ mono_mb_emit_byte (mb, CEE_LDNULL);
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+static void
+emit_isinst_ilgen (MonoMethodBuilder *mb)
+{
+ int return_null_pos, positive_cache_hit_pos, negative_cache_hit_pos;
+ const int obj_arg_position = TYPECHECK_OBJECT_ARG_POS;
+ const int class_arg_position = TYPECHECK_CLASS_ARG_POS;
+ const int cache_arg_position = TYPECHECK_CACHE_ARG_POS;
+
+ generate_check_cache (obj_arg_position, class_arg_position, cache_arg_position,
+ &return_null_pos, &negative_cache_hit_pos, &positive_cache_hit_pos, mb);
+ // Return the object gotten via the slow path.
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ // return NULL;
+ mono_mb_patch_branch (mb, negative_cache_hit_pos);
+ mono_mb_patch_branch (mb, return_null_pos);
+ mono_mb_emit_byte (mb, CEE_LDNULL);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ // return obj
+ mono_mb_patch_branch (mb, positive_cache_hit_pos);
+ mono_mb_emit_ldarg (mb, obj_arg_position);
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+static void
+load_array_element_address (MonoMethodBuilder *mb)
+{
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldarg (mb, 1);
+ mono_mb_emit_op (mb, CEE_LDELEMA, mono_defaults.object_class);
+}
+
+static void
+load_array_class (MonoMethodBuilder *mb, int aklass)
+{
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_ldflda (mb, m_class_offsetof_element_class ());
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, aklass);
+}
+
+static void
+load_value_class (MonoMethodBuilder *mb, int vklass)
+{
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, vklass);
+}
+
+
+
+static int
+emit_marshal_scalar_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
+ MonoMarshalSpec *spec, int conv_arg,
+ MonoType **conv_arg_type, MarshalAction action)
+{
+ MonoMethodBuilder *mb = m->mb;
+
+ switch (action) {
+ case MARSHAL_ACTION_PUSH:
+ mono_mb_emit_ldarg (mb, argnum);
+ break;
+
+ case MARSHAL_ACTION_CONV_RESULT:
+ /* no conversions necessary */
+ mono_mb_emit_stloc (mb, 3);
+ break;
+
+ case MARSHAL_ACTION_MANAGED_CONV_RESULT:
+ mono_mb_emit_stloc (mb, 3);
+ break;
+
+ default:
+ break;
+ }
+ return conv_arg;
+}
+
+
+static void
+emit_virtual_stelemref_ilgen (MonoMethodBuilder *mb, const char **param_names, MonoStelemrefKind kind)
+{
+ guint32 b1, b2, b3, b4;
+ int aklass, vklass, vtable, uiid;
+ int array_slot_addr;
+
+ mono_mb_set_param_names (mb, param_names);
+ MonoType *int_type = mono_get_int_type ();
+ MonoType *int32_type = m_class_get_byval_arg (mono_defaults.int32_class);
+ MonoType *object_type_byref = mono_class_get_byref_type (mono_defaults.object_class);
+
+ /*For now simply call plain old stelemref*/
+ switch (kind) {
+ case STELEMREF_OBJECT:
+ /* ldelema (implicit bound check) */
+ load_array_element_address (mb);
+ /* do_store */
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+ mono_mb_emit_byte (mb, CEE_RET);
+ break;
+
+ case STELEMREF_COMPLEX: {
+ int b_fast;
+ /*
+ <ldelema (bound check)>
+ if (!value)
+ goto store;
+ if (!mono_object_isinst (value, aklass))
+ goto do_exception;
+
+ do_store:
+ *array_slot_addr = value;
+
+ do_exception:
+ throw new ArrayTypeMismatchException ();
+ */
+
+ aklass = mono_mb_add_local (mb, int_type);
+ vklass = mono_mb_add_local (mb, int_type);
+ array_slot_addr = mono_mb_add_local (mb, object_type_byref);
+
+#if 0
+ {
+ /*Use this to debug/record stores that are going thru the slow path*/
+ MonoMethodSignature *csig;
+ csig = mono_metadata_signature_alloc (mono_defaults.corlib, 3);
+ csig->ret = mono_get_void_type ();
+ csig->params [0] = object_type;
+ csig->params [1] = int_type; /* this is a natural sized int */
+ csig->params [2] = object_type;
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldarg (mb, 1);
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_native_call (mb, csig, record_slot_vstore);
+ }
+#endif
+
+ /* ldelema (implicit bound check) */
+ load_array_element_address (mb);
+ mono_mb_emit_stloc (mb, array_slot_addr);
+
+ /* if (!value) goto do_store */
+ mono_mb_emit_ldarg (mb, 2);
+ b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /* aklass = array->vtable->klass->element_class */
+ load_array_class (mb, aklass);
+ /* vklass = value->vtable->klass */
+ load_value_class (mb, vklass);
+
+ /* fastpath */
+ mono_mb_emit_ldloc (mb, vklass);
+ mono_mb_emit_ldloc (mb, aklass);
+ b_fast = mono_mb_emit_branch (mb, CEE_BEQ);
+
+ /*if (mono_object_isinst (value, aklass)) */
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_ldloc (mb, aklass);
+ mono_mb_emit_icall (mb, mono_object_isinst_icall);
+ b2 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /* do_store: */
+ mono_mb_patch_branch (mb, b1);
+ mono_mb_patch_branch (mb, b_fast);
+ mono_mb_emit_ldloc (mb, array_slot_addr);
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /* do_exception: */
+ mono_mb_patch_branch (mb, b2);
+
+ mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
+ break;
+ }
+ case STELEMREF_SEALED_CLASS:
+ /*
+ <ldelema (bound check)>
+ if (!value)
+ goto store;
+
+ aklass = array->vtable->m_class_get_element_class (klass);
+ vklass = value->vtable->klass;
+
+ if (vklass != aklass)
+ goto do_exception;
+
+ do_store:
+ *array_slot_addr = value;
+
+ do_exception:
+ throw new ArrayTypeMismatchException ();
+ */
+ aklass = mono_mb_add_local (mb, int_type);
+ vklass = mono_mb_add_local (mb, int_type);
+ array_slot_addr = mono_mb_add_local (mb, object_type_byref);
+
+ /* ldelema (implicit bound check) */
+ load_array_element_address (mb);
+ mono_mb_emit_stloc (mb, array_slot_addr);
+
+ /* if (!value) goto do_store */
+ mono_mb_emit_ldarg (mb, 2);
+ b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /* aklass = array->vtable->klass->element_class */
+ load_array_class (mb, aklass);
+
+ /* vklass = value->vtable->klass */
+ load_value_class (mb, vklass);
+
+ /*if (vklass != aklass) goto do_exception; */
+ mono_mb_emit_ldloc (mb, aklass);
+ mono_mb_emit_ldloc (mb, vklass);
+ b2 = mono_mb_emit_branch (mb, CEE_BNE_UN);
+
+ /* do_store: */
+ mono_mb_patch_branch (mb, b1);
+ mono_mb_emit_ldloc (mb, array_slot_addr);
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /* do_exception: */
+ mono_mb_patch_branch (mb, b2);
+ mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
+ break;
+
+ case STELEMREF_CLASS: {
+ /*
+ the method:
+ <ldelema (bound check)>
+ if (!value)
+ goto do_store;
+
+ aklass = array->vtable->m_class_get_element_class (klass);
+ vklass = value->vtable->klass;
+
+ if (vklass->idepth < aklass->idepth)
+ goto do_exception;
+
+ if (vklass->supertypes [aklass->idepth - 1] != aklass)
+ goto do_exception;
+
+ do_store:
+ *array_slot_addr = value;
+ return;
+
+ long:
+ throw new ArrayTypeMismatchException ();
+ */
+ aklass = mono_mb_add_local (mb, int_type);
+ vklass = mono_mb_add_local (mb, int_type);
+ array_slot_addr = mono_mb_add_local (mb, object_type_byref);
+
+ /* ldelema (implicit bound check) */
+ load_array_element_address (mb);
+ mono_mb_emit_stloc (mb, array_slot_addr);
+
+ /* if (!value) goto do_store */
+ mono_mb_emit_ldarg (mb, 2);
+ b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /* aklass = array->vtable->klass->element_class */
+ load_array_class (mb, aklass);
+
+ /* vklass = value->vtable->klass */
+ load_value_class (mb, vklass);
+
+ /* if (vklass->idepth < aklass->idepth) goto failue */
+ mono_mb_emit_ldloc (mb, vklass);
+ mono_mb_emit_ldflda (mb, m_class_offsetof_idepth ());
+ mono_mb_emit_byte (mb, CEE_LDIND_U2);
+
+ mono_mb_emit_ldloc (mb, aklass);
+ mono_mb_emit_ldflda (mb, m_class_offsetof_idepth ());
+ mono_mb_emit_byte (mb, CEE_LDIND_U2);
+
+ b3 = mono_mb_emit_branch (mb, CEE_BLT_UN);
+
+ /* if (vklass->supertypes [aklass->idepth - 1] != aklass) goto failure */
+ mono_mb_emit_ldloc (mb, vklass);
+ mono_mb_emit_ldflda (mb, m_class_offsetof_supertypes ());
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+
+ mono_mb_emit_ldloc (mb, aklass);
+ mono_mb_emit_ldflda (mb, m_class_offsetof_idepth ());
+ mono_mb_emit_byte (mb, CEE_LDIND_U2);
+ mono_mb_emit_icon (mb, 1);
+ mono_mb_emit_byte (mb, CEE_SUB);
+ mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P);
+ mono_mb_emit_byte (mb, CEE_MUL);
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+
+ mono_mb_emit_ldloc (mb, aklass);
+ b4 = mono_mb_emit_branch (mb, CEE_BNE_UN);
+
+ /* do_store: */
+ mono_mb_patch_branch (mb, b1);
+ mono_mb_emit_ldloc (mb, array_slot_addr);
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /* do_exception: */
+ mono_mb_patch_branch (mb, b3);
+ mono_mb_patch_branch (mb, b4);
+
+ mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
+ break;
+ }
+
+ case STELEMREF_CLASS_SMALL_IDEPTH:
+ /*
+ the method:
+ <ldelema (bound check)>
+ if (!value)
+ goto do_store;
+
+ aklass = array->vtable->m_class_get_element_class (klass);
+ vklass = value->vtable->klass;
+
+ if (vklass->supertypes [aklass->idepth - 1] != aklass)
+ goto do_exception;
+
+ do_store:
+ *array_slot_addr = value;
+ return;
+
+ long:
+ throw new ArrayTypeMismatchException ();
+ */
+ aklass = mono_mb_add_local (mb, int_type);
+ vklass = mono_mb_add_local (mb, int_type);
+ array_slot_addr = mono_mb_add_local (mb, object_type_byref);
+
+ /* ldelema (implicit bound check) */
+ load_array_element_address (mb);
+ mono_mb_emit_stloc (mb, array_slot_addr);
+
+ /* if (!value) goto do_store */
+ mono_mb_emit_ldarg (mb, 2);
+ b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /* aklass = array->vtable->klass->element_class */
+ load_array_class (mb, aklass);
+
+ /* vklass = value->vtable->klass */
+ load_value_class (mb, vklass);
+
+ /* if (vklass->supertypes [aklass->idepth - 1] != aklass) goto failure */
+ mono_mb_emit_ldloc (mb, vklass);
+ mono_mb_emit_ldflda (mb, m_class_offsetof_supertypes ());
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+
+ mono_mb_emit_ldloc (mb, aklass);
+ mono_mb_emit_ldflda (mb, m_class_offsetof_idepth ());
+ mono_mb_emit_byte (mb, CEE_LDIND_U2);
+ mono_mb_emit_icon (mb, 1);
+ mono_mb_emit_byte (mb, CEE_SUB);
+ mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P);
+ mono_mb_emit_byte (mb, CEE_MUL);
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+
+ mono_mb_emit_ldloc (mb, aklass);
+ b4 = mono_mb_emit_branch (mb, CEE_BNE_UN);
+
+ /* do_store: */
+ mono_mb_patch_branch (mb, b1);
+ mono_mb_emit_ldloc (mb, array_slot_addr);
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /* do_exception: */
+ mono_mb_patch_branch (mb, b4);
+
+ mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
+ break;
+
+ case STELEMREF_INTERFACE:
+ /*Mono *klass;
+ MonoVTable *vt;
+ unsigned uiid;
+ if (value == NULL)
+ goto store;
+
+ klass = array->obj.vtable->klass->element_class;
+ vt = value->vtable;
+ uiid = klass->interface_id;
+ if (uiid > vt->max_interface_id)
+ goto exception;
+ if (!(vt->interface_bitmap [(uiid) >> 3] & (1 << ((uiid)&7))))
+ goto exception;
+ store:
+ mono_array_setref_internal (array, index, value);
+ return;
+ exception:
+ mono_raise_exception (mono_get_exception_array_type_mismatch ());*/
+
+ array_slot_addr = mono_mb_add_local (mb, object_type_byref);
+ aklass = mono_mb_add_local (mb, int_type);
+ vtable = mono_mb_add_local (mb, int_type);
+ uiid = mono_mb_add_local (mb, int32_type);
+
+ /* ldelema (implicit bound check) */
+ load_array_element_address (mb);
+ mono_mb_emit_stloc (mb, array_slot_addr);
+
+ /* if (!value) goto do_store */
+ mono_mb_emit_ldarg (mb, 2);
+ b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /* klass = array->vtable->m_class_get_element_class (klass) */
+ load_array_class (mb, aklass);
+
+ /* vt = value->vtable */
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, vtable);
+
+ /* uiid = klass->interface_id; */
+ mono_mb_emit_ldloc (mb, aklass);
+ mono_mb_emit_ldflda (mb, m_class_offsetof_interface_id ());
+ mono_mb_emit_byte (mb, CEE_LDIND_U4);
+ mono_mb_emit_stloc (mb, uiid);
+
+ /*if (uiid > vt->max_interface_id)*/
+ mono_mb_emit_ldloc (mb, uiid);
+ mono_mb_emit_ldloc (mb, vtable);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, max_interface_id));
+ mono_mb_emit_byte (mb, CEE_LDIND_U4);
+ b2 = mono_mb_emit_branch (mb, CEE_BGT_UN);
+
+ /* if (!(vt->interface_bitmap [(uiid) >> 3] & (1 << ((uiid)&7)))) */
+
+ /*vt->interface_bitmap*/
+ mono_mb_emit_ldloc (mb, vtable);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, interface_bitmap));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+
+ /*uiid >> 3*/
+ mono_mb_emit_ldloc (mb, uiid);
+ mono_mb_emit_icon (mb, 3);
+ mono_mb_emit_byte (mb, CEE_SHR_UN);
+
+ /*vt->interface_bitmap [(uiid) >> 3]*/
+ mono_mb_emit_byte (mb, CEE_ADD); /*interface_bitmap is a guint8 array*/
+ mono_mb_emit_byte (mb, CEE_LDIND_U1);
+
+ /*(1 << ((uiid)&7)))*/
+ mono_mb_emit_icon (mb, 1);
+ mono_mb_emit_ldloc (mb, uiid);
+ mono_mb_emit_icon (mb, 7);
+ mono_mb_emit_byte (mb, CEE_AND);
+ mono_mb_emit_byte (mb, CEE_SHL);
+
+ /*bitwise and the whole thing*/
+ mono_mb_emit_byte (mb, CEE_AND);
+ b3 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /* do_store: */
+ mono_mb_patch_branch (mb, b1);
+ mono_mb_emit_ldloc (mb, array_slot_addr);
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /* do_exception: */
+ mono_mb_patch_branch (mb, b2);
+ mono_mb_patch_branch (mb, b3);
+ mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
+ break;
+
+ default:
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldarg (mb, 1);
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_managed_call (mb, mono_marshal_get_stelemref (), NULL);
+ mono_mb_emit_byte (mb, CEE_RET);
+ g_assert (0);
+ }
+}
+
+static void
+emit_stelemref_ilgen (MonoMethodBuilder *mb)
+{
+ guint32 b1, b2, b3, b4;
+ guint32 copy_pos;
+ int aklass, vklass;
+ int array_slot_addr;
+
+ MonoType *int_type = mono_get_int_type ();
+ MonoType *object_type_byref = mono_class_get_byref_type (mono_defaults.object_class);
+
+ aklass = mono_mb_add_local (mb, int_type);
+ vklass = mono_mb_add_local (mb, int_type);
+ array_slot_addr = mono_mb_add_local (mb, object_type_byref);
+
+ /*
+ the method:
+ <ldelema (bound check)>
+ if (!value)
+ goto store;
+
+ aklass = array->vtable->m_class_get_element_class (klass);
+ vklass = value->vtable->klass;
+
+ if (vklass->idepth < aklass->idepth)
+ goto long;
+
+ if (vklass->supertypes [aklass->idepth - 1] != aklass)
+ goto long;
+
+ store:
+ *array_slot_addr = value;
+ return;
+
+ long:
+ if (mono_object_isinst (value, aklass))
+ goto store;
+
+ throw new ArrayTypeMismatchException ();
+ */
+
+ /* ldelema (implicit bound check) */
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldarg (mb, 1);
+ mono_mb_emit_op (mb, CEE_LDELEMA, mono_defaults.object_class);
+ mono_mb_emit_stloc (mb, array_slot_addr);
+
+ /* if (!value) goto do_store */
+ mono_mb_emit_ldarg (mb, 2);
+ b1 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /* aklass = array->vtable->klass->element_class */
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_ldflda (mb, m_class_offsetof_element_class ());
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, aklass);
+
+ /* vklass = value->vtable->klass */
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoObject, vtable));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, vklass);
+
+ /* if (vklass->idepth < aklass->idepth) goto failue */
+ mono_mb_emit_ldloc (mb, vklass);
+ mono_mb_emit_ldflda (mb, m_class_offsetof_idepth ());
+ mono_mb_emit_byte (mb, CEE_LDIND_U2);
+
+ mono_mb_emit_ldloc (mb, aklass);
+ mono_mb_emit_ldflda (mb, m_class_offsetof_idepth ());
+ mono_mb_emit_byte (mb, CEE_LDIND_U2);
+
+ b2 = mono_mb_emit_branch (mb, CEE_BLT_UN);
+
+ /* if (vklass->supertypes [aklass->idepth - 1] != aklass) goto failure */
+ mono_mb_emit_ldloc (mb, vklass);
+ mono_mb_emit_ldflda (mb, m_class_offsetof_supertypes ());
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+
+ mono_mb_emit_ldloc (mb, aklass);
+ mono_mb_emit_ldflda (mb, m_class_offsetof_idepth ());
+ mono_mb_emit_byte (mb, CEE_LDIND_U2);
+ mono_mb_emit_icon (mb, 1);
+ mono_mb_emit_byte (mb, CEE_SUB);
+ mono_mb_emit_icon (mb, TARGET_SIZEOF_VOID_P);
+ mono_mb_emit_byte (mb, CEE_MUL);
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+
+ mono_mb_emit_ldloc (mb, aklass);
+
+ b3 = mono_mb_emit_branch (mb, CEE_BNE_UN);
+
+ copy_pos = mono_mb_get_label (mb);
+ /* do_store */
+ mono_mb_patch_branch (mb, b1);
+ mono_mb_emit_ldloc (mb, array_slot_addr);
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /* the hard way */
+ mono_mb_patch_branch (mb, b2);
+ mono_mb_patch_branch (mb, b3);
+
+ mono_mb_emit_ldarg (mb, 2);
+ mono_mb_emit_ldloc (mb, aklass);
+ mono_mb_emit_icall (mb, mono_object_isinst_icall);
+
+ b4 = mono_mb_emit_branch (mb, CEE_BRTRUE);
+ mono_mb_patch_addr (mb, b4, copy_pos - (b4 + 4));
+ mono_mb_emit_exception (mb, "ArrayTypeMismatchException", NULL);
+
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+static void
+mb_emit_byte_ilgen (MonoMethodBuilder *mb, guint8 op)
+{
+ mono_mb_emit_byte (mb, op);
+}
+
+static void
+emit_array_address_ilgen (MonoMethodBuilder *mb, int rank, int elem_size)
+{
+ int i, bounds, ind, realidx;
+ int branch_pos, *branch_positions;
+
+ MonoType *int_type = mono_get_int_type ();
+ MonoType *int32_type = mono_get_int32_type ();
+
+ branch_positions = g_new0 (int, rank);
+
+ bounds = mono_mb_add_local (mb, int_type);
+ ind = mono_mb_add_local (mb, int32_type);
+ realidx = mono_mb_add_local (mb, int32_type);
+
+ /* bounds = array->bounds; */
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoArray, bounds));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_stloc (mb, bounds);
+
+ /* ind is the overall element index, realidx is the partial index in a single dimension */
+ /* ind = idx0 - bounds [0].lower_bound */
+ mono_mb_emit_ldarg (mb, 1);
+ mono_mb_emit_ldloc (mb, bounds);
+ mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I4);
+ mono_mb_emit_byte (mb, CEE_SUB);
+ mono_mb_emit_stloc (mb, ind);
+ /* if (ind >= bounds [0].length) goto exeception; */
+ mono_mb_emit_ldloc (mb, ind);
+ mono_mb_emit_ldloc (mb, bounds);
+ mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoArrayBounds, length));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I4);
+ /* note that we use unsigned comparison */
+ branch_pos = mono_mb_emit_branch (mb, CEE_BGE_UN);
+
+ /* For large ranks (> 4?) use a loop n IL later to reduce code size.
+ * We could also decide to ignore the passed elem_size and get it
+ * from the array object, to reduce the number of methods we generate:
+ * the additional cost is 3 memory loads and a non-immediate mul.
+ */
+ for (i = 1; i < rank; ++i) {
+ /* realidx = idxi - bounds [i].lower_bound */
+ mono_mb_emit_ldarg (mb, 1 + i);
+ mono_mb_emit_ldloc (mb, bounds);
+ mono_mb_emit_icon (mb, (i * sizeof (MonoArrayBounds)) + MONO_STRUCT_OFFSET (MonoArrayBounds, lower_bound));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I4);
+ mono_mb_emit_byte (mb, CEE_SUB);
+ mono_mb_emit_stloc (mb, realidx);
+ /* if (realidx >= bounds [i].length) goto exeception; */
+ mono_mb_emit_ldloc (mb, realidx);
+ mono_mb_emit_ldloc (mb, bounds);
+ mono_mb_emit_icon (mb, (i * sizeof (MonoArrayBounds)) + MONO_STRUCT_OFFSET (MonoArrayBounds, length));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I4);
+ branch_positions [i] = mono_mb_emit_branch (mb, CEE_BGE_UN);
+ /* ind = ind * bounds [i].length + realidx */
+ mono_mb_emit_ldloc (mb, ind);
+ mono_mb_emit_ldloc (mb, bounds);
+ mono_mb_emit_icon (mb, (i * sizeof (MonoArrayBounds)) + MONO_STRUCT_OFFSET (MonoArrayBounds, length));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I4);
+ mono_mb_emit_byte (mb, CEE_MUL);
+ mono_mb_emit_ldloc (mb, realidx);
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_stloc (mb, ind);
+ }
+
+ /* return array->vector + ind * element_size */
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoArray, vector));
+ mono_mb_emit_ldloc (mb, ind);
+ if (elem_size) {
+ mono_mb_emit_icon (mb, elem_size);
+ } else {
+ /* Load arr->vtable->klass->sizes.element_class */
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_byte (mb, CEE_CONV_I);
+ mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoObject, vtable));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ /* sizes is an union, so this reads sizes.element_size */
+ mono_mb_emit_icon (mb, m_class_offsetof_sizes ());
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_LDIND_I4);
+ }
+ mono_mb_emit_byte (mb, CEE_MUL);
+ mono_mb_emit_byte (mb, CEE_ADD);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /* patch the branches to get here and throw */
+ for (i = 1; i < rank; ++i) {
+ mono_mb_patch_branch (mb, branch_positions [i]);
+ }
+ mono_mb_patch_branch (mb, branch_pos);
+ /* throw exception */
+ mono_mb_emit_exception (mb, "IndexOutOfRangeException", NULL);
+
+ g_free (branch_positions);
+}
+
+static void
+emit_delegate_begin_invoke_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig)
+{
+ int params_var;
+ params_var = mono_mb_emit_save_args (mb, sig, FALSE);
+
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldloc (mb, params_var);
+ mono_mb_emit_icall (mb, mono_delegate_begin_invoke);
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+static void
+emit_delegate_end_invoke_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig)
+{
+ int params_var;
+ params_var = mono_mb_emit_save_args (mb, sig, FALSE);
+
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldloc (mb, params_var);
+ mono_mb_emit_icall (mb, mono_delegate_end_invoke);
+
+ if (sig->ret->type == MONO_TYPE_VOID) {
+ mono_mb_emit_byte (mb, CEE_POP);
+ mono_mb_emit_byte (mb, CEE_RET);
+ } else
+ mono_mb_emit_restore_result (mb, sig->ret);
+}
+
+static void
+emit_delegate_invoke_internal_ilgen (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)
+{
+ int local_i, local_len, local_delegates, local_d, local_target, local_res = 0;
+ int pos0, pos1, pos2;
+ int i;
+ gboolean void_ret;
+
+ MonoType *int32_type = mono_get_int32_type ();
+ MonoType *object_type = mono_get_object_type ();
+
+ void_ret = sig->ret->type == MONO_TYPE_VOID && !method->string_ctor;
+
+ /* allocate local 0 (object) */
+ local_i = mono_mb_add_local (mb, int32_type);
+ local_len = mono_mb_add_local (mb, int32_type);
+ local_delegates = mono_mb_add_local (mb, m_class_get_byval_arg (mono_defaults.array_class));
+ local_d = mono_mb_add_local (mb, m_class_get_byval_arg (mono_defaults.multicastdelegate_class));
+ local_target = mono_mb_add_local (mb, object_type);
+
+ if (!void_ret)
+ local_res = mono_mb_add_local (mb, m_class_get_byval_arg (mono_class_from_mono_type_internal (sig->ret)));
+
+ g_assert (sig->hasthis);
+
+ /*
+ * {type: sig->ret} res;
+ * if (delegates == null) {
+ * return this.<target> ( args .. );
+ * } else {
+ * int i = 0, len = this.delegates.Length;
+ * do {
+ * res = this.delegates [i].Invoke ( args .. );
+ * } while (++i < len);
+ * return res;
+ * }
+ */
+
+ /* this wrapper can be used in unmanaged-managed transitions */
+ emit_thread_interrupt_checkpoint (mb);
+
+ /* delegates = this.delegates */
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoMulticastDelegate, delegates));
+ mono_mb_emit_byte (mb, CEE_LDIND_REF);
+ mono_mb_emit_stloc (mb, local_delegates);
+
+ /* if (delegates == null) */
+ mono_mb_emit_ldloc (mb, local_delegates);
+ pos2 = mono_mb_emit_branch (mb, CEE_BRTRUE);
+
+ /* return target.<target_method|method_ptr> ( args .. ); */
+
+ /* target = d.target; */
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, target));
+ mono_mb_emit_byte (mb, CEE_LDIND_REF);
+ mono_mb_emit_stloc (mb, local_target);
+
+ /*static methods with bound first arg can have null target and still be bound*/
+ if (!static_method_with_first_arg_bound) {
+ /* if target != null */
+ mono_mb_emit_ldloc (mb, local_target);
+ pos0 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+
+ /* then call this->method_ptr nonstatic */
+ if (callvirt) {
+ // FIXME:
+ mono_mb_emit_exception_full (mb, "System", "NotImplementedException", "");
+ } else {
+ mono_mb_emit_ldloc (mb, local_target);
+ for (i = 0; i < sig->param_count; ++i)
+ mono_mb_emit_ldarg (mb, i + 1);
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, extra_arg));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_LD_DELEGATE_METHOD_PTR);
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_op (mb, CEE_MONO_CALLI_EXTRA_ARG, sig);
+ mono_mb_emit_byte (mb, CEE_RET);
+ }
+
+ /* else [target == null] call this->method_ptr static */
+ mono_mb_patch_branch (mb, pos0);
+ }
+
+ if (callvirt) {
+ if (!closed_over_null) {
+ /* if target_method is not really virtual, turn it into a direct call */
+ if (!(target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) || m_class_is_valuetype (target_class)) {
+ mono_mb_emit_ldarg (mb, 1);
+ for (i = 1; i < sig->param_count; ++i)
+ mono_mb_emit_ldarg (mb, i + 1);
+ mono_mb_emit_op (mb, CEE_CALL, target_method);
+ } else {
+ mono_mb_emit_ldarg (mb, 1);
+ mono_mb_emit_op (mb, CEE_CASTCLASS, target_class);
+ for (i = 1; i < sig->param_count; ++i)
+ mono_mb_emit_ldarg (mb, i + 1);
+ mono_mb_emit_op (mb, CEE_CALLVIRT, target_method);
+ }
+ } else {
+ mono_mb_emit_byte (mb, CEE_LDNULL);
+ for (i = 0; i < sig->param_count; ++i)
+ mono_mb_emit_ldarg (mb, i + 1);
+ mono_mb_emit_op (mb, CEE_CALL, target_method);
+ }
+ } else {
+ if (static_method_with_first_arg_bound) {
+ mono_mb_emit_ldloc (mb, local_target);
+ if (!MONO_TYPE_IS_REFERENCE (invoke_sig->params[0]))
+ mono_mb_emit_op (mb, CEE_UNBOX_ANY, mono_class_from_mono_type_internal (invoke_sig->params[0]));
+ }
+ for (i = 0; i < sig->param_count; ++i)
+ mono_mb_emit_ldarg (mb, i + 1);
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoDelegate, extra_arg));
+ mono_mb_emit_byte (mb, CEE_LDIND_I);
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_LD_DELEGATE_METHOD_PTR);
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_op (mb, CEE_MONO_CALLI_EXTRA_ARG, invoke_sig);
+ }
+
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ /* else [delegates != null] */
+ mono_mb_patch_branch (mb, pos2);
+
+ /* len = delegates.Length; */
+ mono_mb_emit_ldloc (mb, local_delegates);
+ mono_mb_emit_byte (mb, CEE_LDLEN);
+ mono_mb_emit_byte (mb, CEE_CONV_I4);
+ mono_mb_emit_stloc (mb, local_len);
+
+ /* i = 0; */
+ mono_mb_emit_icon (mb, 0);
+ mono_mb_emit_stloc (mb, local_i);
+
+ pos1 = mono_mb_get_label (mb);
+
+ /* d = delegates [i]; */
+ mono_mb_emit_ldloc (mb, local_delegates);
+ mono_mb_emit_ldloc (mb, local_i);
+ mono_mb_emit_byte (mb, CEE_LDELEM_REF);
+ mono_mb_emit_stloc (mb, local_d);
+
+ /* res = d.Invoke ( args .. ); */
+ mono_mb_emit_ldloc (mb, local_d);
+ for (i = 0; i < sig->param_count; i++)
+ mono_mb_emit_ldarg (mb, i + 1);
+ if (!ctx) {
+ mono_mb_emit_op (mb, CEE_CALLVIRT, method);
+ } else {
+ ERROR_DECL (error);
+ mono_mb_emit_op (mb, CEE_CALLVIRT, mono_class_inflate_generic_method_checked (method, &container->context, error));
+ g_assert (is_ok (error)); /* FIXME don't swallow the error */
+ }
+
+ if (!void_ret)
+ mono_mb_emit_stloc (mb, local_res);
+
+ /* i += 1 */
+ mono_mb_emit_add_to_local (mb, local_i, 1);
+
+ /* i < l */
+ mono_mb_emit_ldloc (mb, local_i);
+ mono_mb_emit_ldloc (mb, local_len);
+ mono_mb_emit_branch_label (mb, CEE_BLT, pos1);
+
+ /* return res */
+ if (!void_ret)
+ mono_mb_emit_ldloc (mb, local_res);
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+static void
+mb_skip_visibility_ilgen (MonoMethodBuilder *mb)
+{
+ mb->skip_visibility = 1;
+}
+
+static void
+mb_set_dynamic_ilgen (MonoMethodBuilder *mb)
+{
+ mb->dynamic = 1;
+}
+
+static void
+emit_synchronized_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, MonoGenericContext *ctx, MonoGenericContainer *container, MonoMethod *enter_method, MonoMethod *exit_method, MonoMethod *gettypefromhandle_method)
+{
+ int i, pos, pos2, this_local, taken_local, ret_local = 0;
+ MonoMethodSignature *sig = mono_method_signature_internal (method);
+ MonoExceptionClause *clause;
+
+ /* result */
+ if (!MONO_TYPE_IS_VOID (sig->ret))
+ ret_local = mono_mb_add_local (mb, sig->ret);
+
+ if (m_class_is_valuetype (method->klass) && !(method->flags & MONO_METHOD_ATTR_STATIC)) {
+ /* FIXME Is this really the best way to signal an error here? Isn't this called much later after class setup? -AK */
+ mono_class_set_type_load_failure (method->klass, "");
+ /* This will throw the type load exception when the wrapper is compiled */
+ mono_mb_emit_byte (mb, CEE_LDNULL);
+ mono_mb_emit_op (mb, CEE_ISINST, method->klass);
+ mono_mb_emit_byte (mb, CEE_POP);
+
+ if (!MONO_TYPE_IS_VOID (sig->ret))
+ mono_mb_emit_ldloc (mb, ret_local);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ return;
+ }
+
+ MonoType *object_type = mono_get_object_type ();
+ MonoType *boolean_type = m_class_get_byval_arg (mono_defaults.boolean_class);
+ /* this */
+ this_local = mono_mb_add_local (mb, object_type);
+ taken_local = mono_mb_add_local (mb, boolean_type);
+
+ clause = (MonoExceptionClause *)mono_image_alloc0 (get_method_image (method), sizeof (MonoExceptionClause));
+ clause->flags = MONO_EXCEPTION_CLAUSE_FINALLY;
+
+ /* Push this or the type object */
+ if (method->flags & METHOD_ATTRIBUTE_STATIC) {
+ /* We have special handling for this in the JIT */
+ int index = mono_mb_add_data (mb, method->klass);
+ mono_mb_add_data (mb, mono_defaults.typehandle_class);
+ mono_mb_emit_byte (mb, CEE_LDTOKEN);
+ mono_mb_emit_i4 (mb, index);
+
+ mono_mb_emit_managed_call (mb, gettypefromhandle_method, NULL);
+ }
+ else
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_stloc (mb, this_local);
+
+ clause->try_offset = mono_mb_get_label (mb);
+ /* Call Monitor::Enter() */
+ mono_mb_emit_ldloc (mb, this_local);
+ mono_mb_emit_ldloc_addr (mb, taken_local);
+ mono_mb_emit_managed_call (mb, enter_method, NULL);
+
+ /* Call the method */
+ if (sig->hasthis)
+ mono_mb_emit_ldarg (mb, 0);
+ for (i = 0; i < sig->param_count; i++)
+ mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
+
+ if (ctx) {
+ ERROR_DECL (error);
+ mono_mb_emit_managed_call (mb, mono_class_inflate_generic_method_checked (method, &container->context, error), NULL);
+ g_assert (is_ok (error)); /* FIXME don't swallow the error */
+ } else {
+ mono_mb_emit_managed_call (mb, method, NULL);
+ }
+
+ if (!MONO_TYPE_IS_VOID (sig->ret))
+ mono_mb_emit_stloc (mb, ret_local);
+
+ pos = mono_mb_emit_branch (mb, CEE_LEAVE);
+
+ clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
+ clause->handler_offset = mono_mb_get_label (mb);
+
+ /* Call Monitor::Exit() if needed */
+ mono_mb_emit_ldloc (mb, taken_local);
+ pos2 = mono_mb_emit_branch (mb, CEE_BRFALSE);
+ mono_mb_emit_ldloc (mb, this_local);
+ mono_mb_emit_managed_call (mb, exit_method, NULL);
+ mono_mb_patch_branch (mb, pos2);
+ mono_mb_emit_byte (mb, CEE_ENDFINALLY);
+
+ clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
+
+ mono_mb_patch_branch (mb, pos);
+ if (!MONO_TYPE_IS_VOID (sig->ret))
+ mono_mb_emit_ldloc (mb, ret_local);
+ mono_mb_emit_byte (mb, CEE_RET);
+
+ mono_mb_set_clauses (mb, 1, clause);
+}
+
+static void
+emit_unbox_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method)
+{
+ MonoMethodSignature *sig = mono_method_signature_internal (method);
+
+ mono_mb_emit_ldarg (mb, 0);
+ mono_mb_emit_icon (mb, MONO_ABI_SIZEOF (MonoObject));
+ mono_mb_emit_byte (mb, CEE_ADD);
+ for (int i = 0; i < sig->param_count; ++i)
+ mono_mb_emit_ldarg (mb, i + 1);
+ mono_mb_emit_managed_call (mb, method, NULL);
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+static void
+emit_array_accessor_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *sig, MonoGenericContext *ctx)
+{
+ MonoGenericContainer *container = NULL;
+ /* Call the method */
+ if (sig->hasthis)
+ mono_mb_emit_ldarg (mb, 0);
+ for (int i = 0; i < sig->param_count; i++)
+ mono_mb_emit_ldarg (mb, i + (sig->hasthis == TRUE));
+
+ if (ctx) {
+ ERROR_DECL (error);
+ mono_mb_emit_managed_call (mb, mono_class_inflate_generic_method_checked (method, &container->context, error), NULL);
+ g_assert (is_ok (error)); /* FIXME don't swallow the error */
+ } else {
+ mono_mb_emit_managed_call (mb, method, NULL);
+ }
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+static void
+emit_generic_array_helper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *csig)
+{
+ mono_mb_emit_ldarg (mb, 0);
+ for (int i = 0; i < csig->param_count; i++)
+ mono_mb_emit_ldarg (mb, i + 1);
+ mono_mb_emit_managed_call (mb, method, NULL);
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+static void
+emit_thunk_invoke_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *csig)
+{
+ MonoImage *image = get_method_image (method);
+ MonoMethodSignature *sig = mono_method_signature_internal (method);
+ int param_count = sig->param_count + sig->hasthis + 1;
+ int pos_leave, coop_gc_var = 0;
+ MonoExceptionClause *clause;
+ MonoType *object_type = mono_get_object_type ();
+#if defined (TARGET_WASM)
+ const gboolean do_blocking_transition = FALSE;
+#else
+ const gboolean do_blocking_transition = TRUE;
+#endif
+
+ /* local 0 (temp for exception object) */
+ mono_mb_add_local (mb, object_type);
+
+ /* local 1 (temp for result) */
+ if (!MONO_TYPE_IS_VOID (sig->ret))
+ mono_mb_add_local (mb, sig->ret);
+
+ if (do_blocking_transition) {
+ /* local 4, the local to be used when calling the suspend funcs */
+ coop_gc_var = mono_mb_add_local (mb, mono_get_int_type ());
+ }
+
+ /* 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);
+
+ if (do_blocking_transition) {
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_GET_SP);
+ mono_mb_emit_icall (mb, mono_threads_enter_gc_unsafe_region_unbalanced);
+ mono_mb_emit_stloc (mb, coop_gc_var);
+ }
+
+ /* try */
+ clause = (MonoExceptionClause *)mono_image_alloc0 (image, sizeof (MonoExceptionClause));
+ clause->try_offset = mono_mb_get_label (mb);
+
+ /* push method's args */
+ for (int i = 0; i < param_count - 1; i++) {
+ MonoType *type;
+ MonoClass *klass;
+
+ mono_mb_emit_ldarg (mb, i);
+
+ /* get the byval type of the param */
+ klass = mono_class_from_mono_type_internal (csig->params [i]);
+ type = m_class_get_byval_arg (klass);
+
+ /* unbox struct args */
+ if (MONO_TYPE_ISSTRUCT (type)) {
+ mono_mb_emit_op (mb, CEE_UNBOX, klass);
+
+ /* byref args & and the "this" arg must remain a ptr.
+ Otherwise make a copy of the value type */
+ if (!(m_type_is_byref (csig->params [i]) || (i == 0 && sig->hasthis)))
+ mono_mb_emit_op (mb, CEE_LDOBJ, klass);
+
+ csig->params [i] = object_type;
+ }
+ }
+
+ /* 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_set_clauses (mb, 1, clause);
+
+ mono_mb_patch_branch (mb, pos_leave);
+ /* end-try */
+
+ if (!MONO_TYPE_IS_VOID (sig->ret)) {
+ mono_mb_emit_ldloc (mb, 1);
+
+ /* box the return value */
+ if (MONO_TYPE_ISSTRUCT (sig->ret))
+ mono_mb_emit_op (mb, CEE_BOX, mono_class_from_mono_type_internal (sig->ret));
+ }
+
+ if (do_blocking_transition) {
+ mono_mb_emit_ldloc (mb, coop_gc_var);
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_GET_SP);
+ mono_mb_emit_icall (mb, mono_threads_exit_gc_unsafe_region_unbalanced);
+ }
+
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+static gboolean
+emit_managed_wrapper_validate_signature (MonoMethodSignature* sig, MonoMarshalSpec** mspecs, MonoError* error)
+{
+ if (mspecs) {
+ for (int i = 0; i < sig->param_count; i ++) {
+ if (mspecs [i + 1] && mspecs [i + 1]->native == MONO_NATIVE_CUSTOM) {
+ if (!mspecs [i + 1]->data.custom_data.custom_name || *mspecs [i + 1]->data.custom_data.custom_name == '\0') {
+ mono_error_set_generic_error (error, "System", "TypeLoadException", "Missing ICustomMarshaler type");
+ return FALSE;
+ }
+
+ switch (sig->params[i]->type) {
+ case MONO_TYPE_OBJECT:
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_VALUETYPE:
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY:
+ case MONO_TYPE_STRING:
+ case MONO_TYPE_BOOLEAN:
+ break;
+ default:
+ mono_error_set_generic_error (error, "System.Runtime.InteropServices", "MarshalDirectiveException", "custom marshalling of type %x is currently not supported", sig->params[i]->type);
+ return FALSE;
+ }
+ } else if (sig->params[i]->type == MONO_TYPE_VALUETYPE) {
+ MonoClass *klass = mono_class_from_mono_type_internal (sig->params [i]);
+ MonoMarshalType *marshal_type = mono_marshal_load_type_info (klass);
+ for (int field_idx = 0; field_idx < marshal_type->num_fields; ++field_idx) {
+ if (marshal_type->fields [field_idx].mspec && marshal_type->fields [field_idx].mspec->native == MONO_NATIVE_CUSTOM) {
+ mono_error_set_type_load_class (error, klass, "Value type includes custom marshaled fields");
+ return FALSE;
+ }
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+emit_managed_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *invoke_sig, MonoMarshalSpec **mspecs, EmitMarshalContext* m, MonoMethod *method, MonoGCHandle target_handle, MonoError *error)
+{
+ MonoMethodSignature *sig, *csig;
+ int i, *tmp_locals, orig_domain, attach_cookie;
+ gboolean closed = FALSE;
+
+ sig = m->sig;
+ csig = m->csig;
+
+ if (!sig->hasthis && sig->param_count != invoke_sig->param_count) {
+ /* Closed delegate */
+ g_assert (sig->param_count == invoke_sig->param_count + 1);
+ closed = TRUE;
+ /* Use a new signature without the first argument */
+ sig = mono_metadata_signature_dup (sig);
+ memmove (&sig->params [0], &sig->params [1], (sig->param_count - 1) * sizeof (MonoType*));
+ sig->param_count --;
+ }
+
+ if (!emit_managed_wrapper_validate_signature (sig, mspecs, error)) {
+ if (closed)
+ g_free (sig);
+ return;
+ }
+
+ MonoType *int_type = mono_get_int_type ();
+ MonoType *boolean_type = m_class_get_byval_arg (mono_defaults.boolean_class);
+ /* allocate local 0 (pointer) src_ptr */
+ mono_mb_add_local (mb, int_type);
+ /* allocate local 1 (pointer) dst_ptr */
+ mono_mb_add_local (mb, int_type);
+ /* allocate local 2 (boolean) delete_old */
+ mono_mb_add_local (mb, boolean_type);
+
+ if (!MONO_TYPE_IS_VOID(sig->ret)) {
+ /* allocate local 3 to store the return value */
+ mono_mb_add_local (mb, sig->ret);
+ }
+
+ if (MONO_TYPE_ISSTRUCT (sig->ret))
+ m->vtaddr_var = mono_mb_add_local (mb, int_type);
+
+ orig_domain = mono_mb_add_local (mb, int_type);
+ attach_cookie = mono_mb_add_local (mb, int_type);
+
+ /*
+ * // does (STARTING|RUNNING|BLOCKING) -> RUNNING + set/switch domain
+ * intptr_t attach_cookie;
+ * intptr_t orig_domain = mono_threads_attach_coop (domain, &attach_cookie);
+ * <interrupt check>
+ *
+ * ret = method (...);
+ * // does RUNNING -> (RUNNING|BLOCKING) + unset/switch domain
+ * mono_threads_detach_coop (orig_domain, &attach_cookie);
+ *
+ * return ret;
+ */
+
+ mono_mb_emit_icon (mb, 0);
+ mono_mb_emit_stloc (mb, 2);
+
+ /* orig_domain = mono_threads_attach_coop (domain, &attach_cookie); */
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_LDDOMAIN);
+ mono_mb_emit_ldloc_addr (mb, attach_cookie);
+ /*
+ * This icall is special cased in the JIT so it works in native-to-managed wrappers in unattached threads.
+ * Keep this in sync with the CEE_JIT_ICALL code in the JIT.
+ *
+ * Special cased in interpreter, keep in sync.
+ */
+ mono_mb_emit_icall (mb, mono_threads_attach_coop);
+ mono_mb_emit_stloc (mb, orig_domain);
+
+ /* <interrupt check> */
+ emit_thread_interrupt_checkpoint (mb);
+
+ /* we first do all conversions */
+ tmp_locals = g_newa (int, sig->param_count);
+ for (i = 0; i < sig->param_count; i ++) {
+ MonoType *t = sig->params [i];
+ MonoMarshalSpec *spec = mspecs [i + 1];
+
+ if (spec && spec->native == MONO_NATIVE_CUSTOM) {
+ tmp_locals [i] = mono_emit_marshal (m, i, t, mspecs [i + 1], 0, &csig->params [i], MARSHAL_ACTION_MANAGED_CONV_IN);
+ } else {
+ switch (t->type) {
+ case MONO_TYPE_OBJECT:
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_VALUETYPE:
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY:
+ case MONO_TYPE_STRING:
+ case MONO_TYPE_BOOLEAN:
+ tmp_locals [i] = mono_emit_marshal (m, i, t, mspecs [i + 1], 0, &csig->params [i], MARSHAL_ACTION_MANAGED_CONV_IN);
+ break;
+ default:
+ tmp_locals [i] = 0;
+ break;
+ }
+ }
+ }
+
+ if (sig->hasthis) {
+ if (target_handle) {
+ mono_mb_emit_icon8 (mb, (gint64)target_handle);
+ mono_mb_emit_byte (mb, CEE_CONV_I);
+ mono_mb_emit_icall (mb, mono_gchandle_get_target_internal);
+ } else {
+ /* fixme: */
+ g_assert_not_reached ();
+ }
+ } else if (closed) {
+ mono_mb_emit_icon8 (mb, (gint64)target_handle);
+ mono_mb_emit_byte (mb, CEE_CONV_I);
+ mono_mb_emit_icall (mb, mono_gchandle_get_target_internal);
+ }
+
+ for (i = 0; i < sig->param_count; i++) {
+ MonoType *t = sig->params [i];
+
+ if (tmp_locals [i]) {
+ if (m_type_is_byref (t))
+ mono_mb_emit_ldloc_addr (mb, tmp_locals [i]);
+ else
+ mono_mb_emit_ldloc (mb, tmp_locals [i]);
+ }
+ else
+ mono_mb_emit_ldarg (mb, i);
+ }
+
+ /* ret = method (...) */
+ mono_mb_emit_managed_call (mb, method, NULL);
+
+ if (MONO_TYPE_ISSTRUCT (sig->ret) && sig->ret->type != MONO_TYPE_GENERICINST) {
+ MonoClass *klass = mono_class_from_mono_type_internal (sig->ret);
+ mono_class_init_internal (klass);
+ if (!(mono_class_is_explicit_layout (klass) || m_class_is_blittable (klass))) {
+ /* This is used by get_marshal_cb ()->emit_marshal_vtype (), but it needs to go right before the call */
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_VTADDR);
+ mono_mb_emit_stloc (mb, m->vtaddr_var);
+ }
+ }
+
+ if (mspecs [0] && mspecs [0]->native == MONO_NATIVE_CUSTOM) {
+ mono_emit_marshal (m, 0, sig->ret, mspecs [0], 0, NULL, MARSHAL_ACTION_MANAGED_CONV_RESULT);
+ } else if (!m_type_is_byref (sig->ret)) {
+ switch (sig->ret->type) {
+ case MONO_TYPE_VOID:
+ break;
+ case MONO_TYPE_BOOLEAN:
+ case MONO_TYPE_I1:
+ case MONO_TYPE_U1:
+ case MONO_TYPE_CHAR:
+ case MONO_TYPE_I2:
+ case MONO_TYPE_U2:
+ case MONO_TYPE_I4:
+ case MONO_TYPE_U4:
+ case MONO_TYPE_I:
+ case MONO_TYPE_U:
+ case MONO_TYPE_PTR:
+ case MONO_TYPE_R4:
+ case MONO_TYPE_R8:
+ case MONO_TYPE_I8:
+ case MONO_TYPE_U8:
+ case MONO_TYPE_OBJECT:
+ mono_mb_emit_stloc (mb, 3);
+ break;
+ case MONO_TYPE_STRING:
+ csig->ret = int_type;
+ mono_emit_marshal (m, 0, sig->ret, mspecs [0], 0, NULL, MARSHAL_ACTION_MANAGED_CONV_RESULT);
+ break;
+ case MONO_TYPE_VALUETYPE:
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_SZARRAY:
+ mono_emit_marshal (m, 0, sig->ret, mspecs [0], 0, NULL, MARSHAL_ACTION_MANAGED_CONV_RESULT);
+ break;
+ case MONO_TYPE_GENERICINST: {
+ mono_mb_emit_byte (mb, CEE_POP);
+ break;
+ }
+ default:
+ g_warning ("return type 0x%02x unknown", sig->ret->type);
+ g_assert_not_reached ();
+ }
+ } else {
+ mono_mb_emit_stloc (mb, 3);
+ }
+
+ /* Convert byref arguments back */
+ for (i = 0; i < sig->param_count; i ++) {
+ MonoType *t = sig->params [i];
+ MonoMarshalSpec *spec = mspecs [i + 1];
+
+ if (spec && spec->native == MONO_NATIVE_CUSTOM) {
+ mono_emit_marshal (m, i, t, mspecs [i + 1], tmp_locals [i], NULL, MARSHAL_ACTION_MANAGED_CONV_OUT);
+ }
+ else if (m_type_is_byref (t)) {
+ switch (t->type) {
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_VALUETYPE:
+ case MONO_TYPE_OBJECT:
+ case MONO_TYPE_STRING:
+ case MONO_TYPE_BOOLEAN:
+ mono_emit_marshal (m, i, t, mspecs [i + 1], tmp_locals [i], NULL, MARSHAL_ACTION_MANAGED_CONV_OUT);
+ break;
+ default:
+ break;
+ }
+ }
+ else if (invoke_sig->params [i]->attrs & PARAM_ATTRIBUTE_OUT) {
+ /* The [Out] information is encoded in the delegate signature */
+ switch (t->type) {
+ case MONO_TYPE_SZARRAY:
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_VALUETYPE:
+ mono_emit_marshal (m, i, invoke_sig->params [i], mspecs [i + 1], tmp_locals [i], NULL, MARSHAL_ACTION_MANAGED_CONV_OUT);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+ }
+
+ /* mono_threads_detach_coop (orig_domain, &attach_cookie); */
+ mono_mb_emit_ldloc (mb, orig_domain);
+ mono_mb_emit_ldloc_addr (mb, attach_cookie);
+ /* Special cased in interpreter, keep in sync */
+ mono_mb_emit_icall (mb, mono_threads_detach_coop);
+
+ /* return ret; */
+ if (m->retobj_var) {
+ mono_mb_emit_ldloc (mb, m->retobj_var);
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_op (mb, CEE_MONO_RETOBJ, m->retobj_class);
+ }
+ else {
+ if (!MONO_TYPE_IS_VOID (sig->ret))
+ mono_mb_emit_ldloc (mb, 3);
+ mono_mb_emit_byte (mb, CEE_RET);
+ }
+
+ if (closed)
+ g_free (sig);
+}
+
+static void
+emit_struct_to_ptr_ilgen (MonoMethodBuilder *mb, MonoClass *klass)
+{
+ MonoType *int_type = mono_get_int_type ();
+ MonoType *boolean_type = m_class_get_byval_arg (mono_defaults.boolean_class);
+ if (m_class_is_blittable (klass)) {
+ mono_mb_emit_byte (mb, CEE_LDARG_1);
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+ mono_mb_emit_ldflda (mb, MONO_ABI_SIZEOF (MonoObject));
+ mono_mb_emit_icon (mb, mono_class_value_size (klass, NULL));
+ mono_mb_emit_byte (mb, CEE_PREFIX1);
+ mono_mb_emit_byte (mb, CEE_CPBLK);
+ } else {
+
+ /* allocate local 0 (pointer) src_ptr */
+ mono_mb_add_local (mb, int_type);
+ /* allocate local 1 (pointer) dst_ptr */
+ mono_mb_add_local (mb, int_type);
+ /* allocate local 2 (boolean) delete_old */
+ mono_mb_add_local (mb, boolean_type);
+ mono_mb_emit_byte (mb, CEE_LDARG_2);
+ mono_mb_emit_stloc (mb, 2);
+
+ /* initialize src_ptr to point to the start of object data */
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+ mono_mb_emit_ldflda (mb, MONO_ABI_SIZEOF (MonoObject));
+ mono_mb_emit_stloc (mb, 0);
+
+ /* initialize dst_ptr */
+ mono_mb_emit_byte (mb, CEE_LDARG_1);
+ mono_mb_emit_stloc (mb, 1);
+
+ mono_marshal_shared_emit_struct_conv (mb, klass, FALSE);
+ }
+
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+static void
+emit_ptr_to_struct_ilgen (MonoMethodBuilder *mb, MonoClass *klass)
+{
+ MonoType *int_type = mono_get_int_type ();
+ if (m_class_is_blittable (klass)) {
+ mono_mb_emit_byte (mb, CEE_LDARG_1);
+ mono_mb_emit_ldflda (mb, MONO_ABI_SIZEOF (MonoObject));
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+ mono_mb_emit_icon (mb, mono_class_value_size (klass, NULL));
+ mono_mb_emit_byte (mb, CEE_PREFIX1);
+ mono_mb_emit_byte (mb, CEE_CPBLK);
+ } else {
+
+ /* allocate local 0 (pointer) src_ptr */
+ mono_mb_add_local (mb, int_type);
+ /* allocate local 1 (pointer) dst_ptr */
+ mono_mb_add_local (mb, m_class_get_this_arg (klass));
+
+ /* initialize src_ptr to point to the start of object data */
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+ mono_mb_emit_stloc (mb, 0);
+
+ /* initialize dst_ptr */
+ mono_mb_emit_byte (mb, CEE_LDARG_1);
+ mono_mb_emit_ldflda (mb, MONO_ABI_SIZEOF (MonoObject));
+ mono_mb_emit_stloc (mb, 1);
+
+ mono_marshal_shared_emit_struct_conv (mb, klass, TRUE);
+ }
+
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+static void
+emit_create_string_hack_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *csig, MonoMethod *res)
+{
+ int i;
+
+ g_assert (!mono_method_signature_internal (res)->hasthis);
+ for (i = 1; i <= csig->param_count; i++)
+ mono_mb_emit_ldarg (mb, i);
+ mono_mb_emit_managed_call (mb, res, NULL);
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+/* How the arguments of an icall should be wrapped */
+typedef enum {
+ /* Don't wrap at all, pass the argument as is */
+ ICALL_HANDLES_WRAP_NONE,
+ /* Wrap the argument in an object handle, pass the handle to the icall */
+ ICALL_HANDLES_WRAP_OBJ,
+ /* Wrap the argument in an object handle, pass the handle to the icall,
+ write the value out from the handle when the icall returns */
+ ICALL_HANDLES_WRAP_OBJ_INOUT,
+ /* Initialized an object handle to null, pass to the icalls,
+ write the value out from the handle when the icall returns */
+ ICALL_HANDLES_WRAP_OBJ_OUT,
+ /* Wrap the argument (a valuetype reference) in a handle to pin its
+ enclosing object, but pass the raw reference to the icall. This is
+ also how we pass byref generic parameter arguments to generic method
+ icalls (e.g. System.Array:GetGenericValue_icall<T>(int idx, T out value)) */
+ ICALL_HANDLES_WRAP_VALUETYPE_REF,
+} IcallHandlesWrap;
+
+typedef struct {
+ IcallHandlesWrap wrap;
+ // If wrap is OBJ_OUT or OBJ_INOUT this is >= 0 and holds the referenced managed object,
+ // in case the actual parameter refers to a native frame.
+ // Otherwise it is -1.
+ int handle;
+} IcallHandlesLocal;
+
+/*
+ * Describes how to wrap the given parameter.
+ *
+ */
+static IcallHandlesWrap
+signature_param_uses_handles (MonoMethodSignature *sig, MonoMethodSignature *generic_sig, int param)
+{
+ /* If there is a generic parameter that isn't passed byref, we don't
+ * know how to pass it to an icall that expects some arguments to be
+ * wrapped in handles: if the actual argument type is a reference type
+ * we'd need to wrap it in a handle, otherwise we'd want to pass it as is.
+ */
+ /* FIXME: We should eventually relax the assertion, below, to
+ * allow generic parameters that are constrained to be reference types.
+ */
+ g_assert (!generic_sig || !mono_type_is_generic_parameter (generic_sig->params [param]));
+
+ /* If the parameter in the generic version of the method signature is a
+ * byref type variable T&, pass the corresponding argument by pinning
+ * the memory and passing the raw pointer to the icall. Note that we
+ * do this even if the actual instantiation is a byref reference type
+ * like string& since the C code for the icall has to work uniformly
+ * for both valuetypes and reference types.
+ */
+ if (generic_sig && m_type_is_byref (generic_sig->params [param]) &&
+ (generic_sig->params [param]->type == MONO_TYPE_VAR || generic_sig->params [param]->type == MONO_TYPE_MVAR))
+ return ICALL_HANDLES_WRAP_VALUETYPE_REF;
+
+ if (MONO_TYPE_IS_REFERENCE (sig->params [param])) {
+ if (mono_signature_param_is_out (sig, param))
+ return ICALL_HANDLES_WRAP_OBJ_OUT;
+ else if (m_type_is_byref (sig->params [param]))
+ return ICALL_HANDLES_WRAP_OBJ_INOUT;
+ else
+ return ICALL_HANDLES_WRAP_OBJ;
+ } else if (m_type_is_byref (sig->params [param]))
+ return ICALL_HANDLES_WRAP_VALUETYPE_REF;
+ else
+ return ICALL_HANDLES_WRAP_NONE;
+}
+
+static void
+emit_native_icall_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *csig, gboolean check_exceptions, gboolean aot, MonoMethodPInvoke *piinfo)
+{
+ // FIXME:
+ MonoMethodSignature *call_sig = csig;
+ gboolean uses_handles = FALSE;
+ gboolean foreign_icall = FALSE;
+ IcallHandlesLocal *handles_locals = NULL;
+ MonoMethodSignature *sig = mono_method_signature_internal (method);
+ gboolean need_gc_safe = FALSE;
+ GCSafeTransitionBuilder gc_safe_transition_builder;
+
+ (void) mono_lookup_internal_call_full (method, FALSE, &uses_handles, &foreign_icall);
+
+ if (G_UNLIKELY (foreign_icall)) {
+ /* FIXME: we only want the transitions for hybrid suspend. Q: What to do about AOT? */
+ need_gc_safe = gc_safe_transition_builder_init (&gc_safe_transition_builder, mb, FALSE);
+
+ if (need_gc_safe)
+ gc_safe_transition_builder_add_locals (&gc_safe_transition_builder);
+ }
+
+ if (sig->hasthis) {
+ /*
+ * Add a null check since public icalls can be called with 'call' which
+ * does no such check.
+ */
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+ const int pos = mono_mb_emit_branch (mb, CEE_BRTRUE);
+ mono_mb_emit_exception (mb, "NullReferenceException", NULL);
+ mono_mb_patch_branch (mb, pos);
+ }
+
+ if (uses_handles) {
+ MonoMethodSignature *generic_sig = NULL;
+
+ if (method->is_inflated) {
+ ERROR_DECL (error);
+ MonoMethod *generic_method = ((MonoMethodInflated*)method)->declaring;
+ generic_sig = mono_method_signature_checked (generic_method, error);
+ mono_error_assert_ok (error);
+ }
+
+ // FIXME: The stuff from mono_metadata_signature_dup_internal_with_padding ()
+ call_sig = mono_metadata_signature_alloc (get_method_image (method), csig->param_count);
+ call_sig->param_count = csig->param_count;
+ call_sig->ret = csig->ret;
+ call_sig->pinvoke = csig->pinvoke;
+
+ /* TODO support adding wrappers to non-static struct methods */
+ g_assert (!sig->hasthis || !m_class_is_valuetype (mono_method_get_class (method)));
+
+ handles_locals = g_new0 (IcallHandlesLocal, csig->param_count);
+
+ for (int i = 0; i < csig->param_count; ++i) {
+ // Determine which args need to be wrapped in handles and adjust icall signature.
+ // Here, a handle is a pointer to a volatile local in a managed frame -- which is sufficient and efficient.
+ const IcallHandlesWrap w = signature_param_uses_handles (csig, generic_sig, i);
+ handles_locals [i].wrap = w;
+ int local = -1;
+
+ switch (w) {
+ case ICALL_HANDLES_WRAP_OBJ:
+ case ICALL_HANDLES_WRAP_OBJ_INOUT:
+ case ICALL_HANDLES_WRAP_OBJ_OUT:
+ call_sig->params [i] = mono_class_get_byref_type (mono_class_from_mono_type_internal (csig->params[i]));
+ break;
+ case ICALL_HANDLES_WRAP_NONE:
+ case ICALL_HANDLES_WRAP_VALUETYPE_REF:
+ call_sig->params [i] = csig->params [i];
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ // Add a local var to hold the references for each out arg.
+ switch (w) {
+ case ICALL_HANDLES_WRAP_OBJ_INOUT:
+ case ICALL_HANDLES_WRAP_OBJ_OUT:
+ // FIXME better type
+ local = mono_mb_add_local (mb, mono_get_object_type ());
+
+ if (!mb->volatile_locals) {
+ gpointer mem = mono_image_alloc0 (get_method_image (method), mono_bitset_alloc_size (csig->param_count + 1, 0));
+ mb->volatile_locals = mono_bitset_mem_new (mem, csig->param_count + 1, 0);
+ }
+ mono_bitset_set (mb->volatile_locals, local);
+ break;
+ case ICALL_HANDLES_WRAP_VALUETYPE_REF:
+ case ICALL_HANDLES_WRAP_OBJ:
+ if (!mb->volatile_args) {
+ gpointer mem = mono_image_alloc0 (get_method_image (method), mono_bitset_alloc_size (csig->param_count + 1, 0));
+ mb->volatile_args = mono_bitset_mem_new (mem, csig->param_count + 1, 0);
+ }
+ mono_bitset_set (mb->volatile_args, i);
+ break;
+ case ICALL_HANDLES_WRAP_NONE:
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ handles_locals [i].handle = local;
+
+ // Load each argument. References into the managed heap get wrapped in handles.
+ // Handles here are just pointers to managed volatile locals.
+ switch (w) {
+ case ICALL_HANDLES_WRAP_NONE:
+ case ICALL_HANDLES_WRAP_VALUETYPE_REF:
+ // argI = argI
+ mono_mb_emit_ldarg (mb, i);
+ break;
+ case ICALL_HANDLES_WRAP_OBJ:
+ // argI = &argI_raw
+ mono_mb_emit_ldarg_addr (mb, i);
+ break;
+ case ICALL_HANDLES_WRAP_OBJ_INOUT:
+ case ICALL_HANDLES_WRAP_OBJ_OUT:
+ // If parameter guaranteeably referred to a managed frame,
+ // then could just be passthrough and volatile. Since
+ // that cannot be guaranteed, use a managed volatile local intermediate.
+ // ObjOut:
+ // localI = NULL
+ // ObjInOut:
+ // localI = *argI_raw
+ // &localI
+ if (w == ICALL_HANDLES_WRAP_OBJ_OUT) {
+ mono_mb_emit_byte (mb, CEE_LDNULL);
+ } else {
+ mono_mb_emit_ldarg (mb, i);
+ mono_mb_emit_byte (mb, CEE_LDIND_REF);
+ }
+ mono_mb_emit_stloc (mb, local);
+ mono_mb_emit_ldloc_addr (mb, local);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+ } else {
+ for (int i = 0; i < csig->param_count; i++)
+ mono_mb_emit_ldarg (mb, i);
+ }
+
+ if (need_gc_safe)
+ gc_safe_transition_builder_emit_enter (&gc_safe_transition_builder, &piinfo->method, aot);
+
+ if (aot) {
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_op (mb, CEE_MONO_ICALL_ADDR, &piinfo->method);
+ mono_mb_emit_calli (mb, call_sig);
+ } else {
+ g_assert (piinfo->addr);
+ mono_mb_emit_native_call (mb, call_sig, piinfo->addr);
+ }
+
+ if (need_gc_safe)
+ gc_safe_transition_builder_emit_exit (&gc_safe_transition_builder);
+
+ // Copy back ObjOut and ObjInOut from locals through parameters.
+ if (mb->volatile_locals) {
+ g_assert (handles_locals);
+ for (int i = 0; i < csig->param_count; i++) {
+ const int local = handles_locals [i].handle;
+ if (local >= 0) {
+ // *argI_raw = localI
+ mono_mb_emit_ldarg (mb, i);
+ mono_mb_emit_ldloc (mb, local);
+ mono_mb_emit_byte (mb, CEE_STIND_REF);
+ }
+ }
+ }
+ g_free (handles_locals);
+
+ if (need_gc_safe)
+ gc_safe_transition_builder_cleanup (&gc_safe_transition_builder);
+
+ if (check_exceptions)
+ emit_thread_interrupt_checkpoint (mb);
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+static void
+mb_emit_exception_ilgen (MonoMethodBuilder *mb, const char *exc_nspace, const char *exc_name, const char *msg)
+{
+ mono_mb_emit_exception_full (mb, exc_nspace, exc_name, msg);
+}
+
+static void
+mb_emit_exception_for_error_ilgen (MonoMethodBuilder *mb, const MonoError *error)
+{
+ mono_mb_emit_exception_for_error (mb, (MonoError*)error);
+}
+
+static void
+emit_marshal_directive_exception_ilgen (EmitMarshalContext *m, int argnum, const char* msg)
+{
+ char* fullmsg = NULL;
+ if (argnum == 0)
+ fullmsg = g_strdup_printf("Error marshalling return value: %s", msg);
+ else
+ fullmsg = g_strdup_printf("Error marshalling parameter #%d: %s", argnum, msg);
+
+ mono_marshal_shared_mb_emit_exception_marshal_directive (m->mb, fullmsg);
+}
+
+static void
+emit_vtfixup_ftnptr_ilgen (MonoMethodBuilder *mb, MonoMethod *method, int param_count, guint16 type)
+{
+ for (int i = 0; i < param_count; i++)
+ mono_mb_emit_ldarg (mb, i);
+
+ if (type & VTFIXUP_TYPE_CALL_MOST_DERIVED)
+ mono_mb_emit_op (mb, CEE_CALLVIRT, method);
+ else
+ mono_mb_emit_op (mb, CEE_CALL, method);
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+static void
+emit_icall_wrapper_ilgen (MonoMethodBuilder *mb, MonoJitICallInfo *callinfo, MonoMethodSignature *csig2, gboolean check_exceptions)
+{
+ MonoMethodSignature *const sig = callinfo->sig;
+
+ if (sig->hasthis)
+ mono_mb_emit_byte (mb, CEE_LDARG_0);
+
+ for (int i = 0; i < sig->param_count; i++)
+ mono_mb_emit_ldarg (mb, i + sig->hasthis);
+
+ mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+ mono_mb_emit_byte (mb, CEE_MONO_JIT_ICALL_ADDR);
+ mono_mb_emit_i4 (mb, mono_jit_icall_info_index (callinfo));
+ mono_mb_emit_calli (mb, csig2);
+ if (check_exceptions)
+ emit_thread_interrupt_checkpoint (mb);
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+static void
+emit_return_ilgen (MonoMethodBuilder *mb)
+{
+ mono_mb_emit_byte (mb, CEE_RET);
+}
+
+void
+mono_marshal_lightweight_init (void)
+{
+ MonoMarshalLightweightCallbacks cb;
+ cb.version = MONO_MARSHAL_CALLBACKS_VERSION;
+ cb.emit_marshal_scalar = emit_marshal_scalar_ilgen;
+ cb.emit_castclass = emit_castclass_ilgen;
+ cb.emit_struct_to_ptr = emit_struct_to_ptr_ilgen;
+ cb.emit_ptr_to_struct = emit_ptr_to_struct_ilgen;
+ cb.emit_isinst = emit_isinst_ilgen;
+ cb.emit_virtual_stelemref = emit_virtual_stelemref_ilgen;
+ cb.emit_stelemref = emit_stelemref_ilgen;
+ cb.emit_array_address = emit_array_address_ilgen;
+ cb.emit_native_wrapper = emit_native_wrapper_ilgen;
+ cb.emit_managed_wrapper = emit_managed_wrapper_ilgen;
+ cb.emit_runtime_invoke_body = emit_runtime_invoke_body_ilgen;
+ cb.emit_runtime_invoke_dynamic = emit_runtime_invoke_dynamic_ilgen;
+ cb.emit_delegate_begin_invoke = emit_delegate_begin_invoke_ilgen;
+ cb.emit_delegate_end_invoke = emit_delegate_end_invoke_ilgen;
+ cb.emit_delegate_invoke_internal = emit_delegate_invoke_internal_ilgen;
+ cb.emit_synchronized_wrapper = emit_synchronized_wrapper_ilgen;
+ cb.emit_unbox_wrapper = emit_unbox_wrapper_ilgen;
+ cb.emit_array_accessor_wrapper = emit_array_accessor_wrapper_ilgen;
+ cb.emit_generic_array_helper = emit_generic_array_helper_ilgen;
+ cb.emit_thunk_invoke_wrapper = emit_thunk_invoke_wrapper_ilgen;
+ cb.emit_create_string_hack = emit_create_string_hack_ilgen;
+ cb.emit_native_icall_wrapper = emit_native_icall_wrapper_ilgen;
+ cb.emit_icall_wrapper = emit_icall_wrapper_ilgen;
+ cb.emit_return = emit_return_ilgen;
+ cb.emit_vtfixup_ftnptr = emit_vtfixup_ftnptr_ilgen;
+ cb.mb_skip_visibility = mb_skip_visibility_ilgen;
+ cb.mb_set_dynamic = mb_set_dynamic_ilgen;
+ cb.mb_emit_exception = mb_emit_exception_ilgen;
+ cb.mb_emit_exception_for_error = mb_emit_exception_for_error_ilgen;
+ cb.mb_emit_byte = mb_emit_byte_ilgen;
+ cb.emit_marshal_directive_exception = emit_marshal_directive_exception_ilgen;
+#ifdef DISABLE_NONBLITTABLE
+ mono_marshal_noilgen_init_blittable (&cb);
+#endif
+ mono_install_marshal_callbacks (&cb);
+}