2009-02-04 Mark Probst <mark.probst@gmail.com>
[mono.git] / mono / metadata / marshal.c
index 18504565648ddafed7ae8b002a87d8464e22770c..09a493a41b2e6845edd54f48276a55d676bad919 100644 (file)
@@ -4,11 +4,16 @@
  * Author:
  *   Paolo Molaro (lupus@ximian.com)
  *
- * (C) 2002 Ximian, Inc.  http://www.ximian.com
+ * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
+ * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
  *
  */
 
 #include "config.h"
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+
 #include "object.h"
 #include "loader.h"
 #include "cil-coff.h"
@@ -3832,7 +3837,6 @@ mono_marshal_get_xappdomain_dispatch (MonoMethod *method, int *marshal_types, in
        MonoClass *ret_class = NULL;
        int loc_array=0, loc_return=0, loc_serialized_exc=0;
        MonoExceptionClause *main_clause;
-       MonoMethodHeader *header;
        int pos, pos_leave;
        gboolean copy_return;
 
@@ -4069,13 +4073,11 @@ mono_marshal_get_xappdomain_dispatch (MonoMethod *method, int *marshal_types, in
 
        mono_mb_emit_byte (mb, CEE_RET);
 
+       mono_mb_set_clauses (mb, 1, main_clause);
+
        res = mono_remoting_mb_create_and_cache (method, mb, csig, csig->param_count + 16);
        mono_mb_free (mb);
 
-       header = ((MonoMethodNormal *)res)->header;
-       header->num_clauses = 1;
-       header->clauses = main_clause;
-
        return res;
 }
 
@@ -4671,8 +4673,8 @@ mono_marshal_get_delegate_invoke (MonoMethod *method, MonoDelegate *del)
 
        if (callvirt) {
                // From mono_mb_create_and_cache
+               mb->skip_visibility = 1;
                newm = mono_mb_create_method (mb, sig, sig->param_count + 16);
-               newm->skip_visibility = 1;
                /*We perform double checked locking, so must fence before publishing*/
                mono_memory_barrier ();
                mono_marshal_lock ();
@@ -4690,8 +4692,8 @@ mono_marshal_get_delegate_invoke (MonoMethod *method, MonoDelegate *del)
                        mono_free_method (newm);
                }
        } else {
+               mb->skip_visibility = 1;
                res = mono_mb_create_and_cache (cache, sig, mb, sig, sig->param_count + 16);
-               res->skip_visibility = 1;
        }
        mono_mb_free (mb);
 
@@ -4841,7 +4843,6 @@ mono_marshal_get_runtime_invoke (MonoMethod *method)
 {
        MonoMethodSignature *sig, *csig, *callsig;
        MonoExceptionClause *clause;
-       MonoMethodHeader *header;
        MonoMethodBuilder *mb;
        GHashTable *cache = NULL;
        MonoClass *target_klass;
@@ -4852,6 +4853,7 @@ mono_marshal_get_runtime_invoke (MonoMethod *method)
        int i, pos, posna;
        char *name;
        gboolean need_direct_wrapper = FALSE;
+       int *tmp_nullable_locals;
 
        g_assert (method);
 
@@ -4873,7 +4875,7 @@ mono_marshal_get_runtime_invoke (MonoMethod *method)
        res = mono_marshal_find_in_cache (cache, method);
        if (res)
                return res;
-
+               
        if (method->klass->rank && (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) &&
                (method->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE)) {
                /* 
@@ -4902,6 +4904,12 @@ mono_marshal_get_runtime_invoke (MonoMethod *method)
                }
        }
 
+       /* Vtypes/nullables/Byrefs cause too many problems */
+       for (i = 0; i < callsig->param_count; ++i) {
+               if (MONO_TYPE_ISSTRUCT (callsig->params [i]) || callsig->params [i]->byref)
+                       need_direct_wrapper = TRUE;
+       }
+
        /*
         * We try to share runtime invoke wrappers between different methods but have to
         * be careful about methods whose klass has a type cctor, since putting the wrapper
@@ -5003,6 +5011,8 @@ mono_marshal_get_runtime_invoke (MonoMethod *method)
                }
        }
 
+       tmp_nullable_locals = g_new0 (int, sig->param_count);
+
        for (i = 0; i < sig->param_count; i++) {
                MonoType *t = sig->params [i];
                int type;
@@ -5019,11 +5029,11 @@ mono_marshal_get_runtime_invoke (MonoMethod *method)
                         * 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 (t))) {
-                               int tmp_nullable_local = mono_mb_add_local (mb, &mono_class_from_mono_type (t)->byval_arg);
+                               tmp_nullable_locals [i] = mono_mb_add_local (mb, &mono_class_from_mono_type (t)->byval_arg);
 
                                mono_mb_emit_op (mb, CEE_UNBOX_ANY, mono_class_from_mono_type (t));
-                               mono_mb_emit_stloc (mb, tmp_nullable_local);
-                               mono_mb_emit_ldloc_addr (mb, tmp_nullable_local);
+                               mono_mb_emit_stloc (mb, tmp_nullable_locals [i]);
+                               mono_mb_emit_ldloc_addr (mb, tmp_nullable_locals [i]);
                        }
                        continue;
                }
@@ -5133,6 +5143,26 @@ handle_enum:
        }
 
        mono_mb_emit_stloc (mb, 0);
+
+       /* 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 (t->byref && t->type == MONO_TYPE_GENERICINST && mono_class_is_nullable (mono_class_from_mono_type (t))) {
+                       mono_mb_emit_ldarg (mb, 1);                     
+                       mono_mb_emit_icon (mb, sizeof (gpointer) * 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 (t));
+
+                       mono_mb_emit_byte (mb, CEE_STIND_REF);
+               }
+       }
                        
        pos = mono_mb_emit_branch (mb, CEE_LEAVE);
 
@@ -5179,12 +5209,15 @@ handle_enum:
 
        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);
 
        if (need_direct_wrapper) {
+               mb->skip_visibility = 1;
                res = mono_mb_create_and_cache (cache, method, mb, csig, sig->param_count + 16);
        } else {
                /* taken from mono_mb_create_and_cache */
@@ -5215,10 +5248,6 @@ handle_enum:
 
        mono_mb_free (mb);
 
-       header = ((MonoMethodNormal *)res)->header;
-       header->num_clauses = 1;
-       header->clauses = clause;
-
        return res;     
 }
 
@@ -5268,9 +5297,9 @@ mono_marshal_get_static_rgctx_invoke (MonoMethod *method)
        mono_mb_emit_op (mb, CEE_CALL, method);
        mono_mb_emit_byte (mb, CEE_RET);
 
+       mb->skip_visibility = TRUE;
        res = mono_mb_create_and_cache (cache, method, mb, mono_method_signature (method),
                sig->param_count + sig->hasthis + 4);
-       res->skip_visibility = TRUE;
        res->flags = method->flags;
 
        mono_mb_free (mb);
@@ -6069,7 +6098,7 @@ emit_marshal_custom (EmitMarshalContext *m, int argnum, MonoType *t,
                        mono_mb_emit_ldloc (mb, conv_arg);
                        mono_mb_emit_op (mb, CEE_CALLVIRT, marshal_native_to_managed);
                        mono_mb_emit_byte (mb, CEE_STIND_REF);
-               } else {
+               } else if (t->attrs & PARAM_ATTRIBUTE_OUT) {
                        mono_mb_emit_ldstr (mb, g_strdup (spec->data.custom_data.cookie));
 
                        mono_mb_emit_op (mb, CEE_CALL, get_instance);
@@ -8479,7 +8508,7 @@ emit_marshal_ptr (EmitMarshalContext *m, int argnum, MonoType *t,
 
        switch (action) {
        case MARSHAL_ACTION_CONV_IN:
-               if (MONO_TYPE_ISSTRUCT (t->data.type)) {
+               if (MONO_TYPE_ISSTRUCT (t->data.type) && !mono_class_from_mono_type (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_mb_emit_exception_marshal_directive (m->mb, msg);
                }
@@ -9821,7 +9850,6 @@ mono_marshal_get_synchronized_wrapper (MonoMethod *method)
        static MonoMethod *enter_method, *exit_method, *gettypefromhandle_method;
        MonoMethodSignature *sig;
        MonoExceptionClause *clause;
-       MonoMethodHeader *header;
        MonoMethodBuilder *mb;
        MonoMethod *res;
        GHashTable *cache;
@@ -9871,6 +9899,8 @@ mono_marshal_get_synchronized_wrapper (MonoMethod *method)
        mono_loader_unlock ();
        clause->flags = MONO_EXCEPTION_CLAUSE_FINALLY;
 
+       mono_loader_lock ();
+
        if (!enter_method) {
                MonoMethodDesc *desc;
 
@@ -9890,6 +9920,8 @@ mono_marshal_get_synchronized_wrapper (MonoMethod *method)
                mono_method_desc_free (desc);
        }
 
+       mono_loader_unlock ();
+
        /* Push this or the type object */
        if (method->flags & METHOD_ATTRIBUTE_STATIC) {
                /* We have special handling for this in the JIT */
@@ -9938,14 +9970,12 @@ mono_marshal_get_synchronized_wrapper (MonoMethod *method)
                mono_mb_emit_ldloc (mb, ret_local);
        mono_mb_emit_byte (mb, CEE_RET);
 
+       mono_mb_set_clauses (mb, 1, clause);
+
        res = mono_mb_create_and_cache (cache, method,
                                                                        mb, sig, sig->param_count + 16);
        mono_mb_free (mb);
 
-       header = ((MonoMethodNormal *)res)->header;
-       header->num_clauses = 1;
-       header->clauses = clause;
-
        return res;     
 }
 
@@ -10294,46 +10324,6 @@ mono_marshal_get_array_address (int rank, int elem_size)
        return ret;
 }
 
-MonoMethod*
-mono_marshal_get_write_barrier (void)
-{
-       static MonoMethod* ret = NULL;
-       MonoMethodSignature *sig;
-       MonoMethodBuilder *mb;
-       int max_stack = 2;
-
-       if (ret)
-               return ret;
-       
-       mb = mono_mb_new (mono_defaults.object_class, "writebarrier", MONO_WRAPPER_WRITE_BARRIER);
-
-       sig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
-
-       /* void writebarrier (MonoObject** addr, MonoObject* obj) */
-       sig->ret = &mono_defaults.void_class->byval_arg;
-       sig->params [0] = &mono_defaults.object_class->this_arg;
-       sig->params [1] = &mono_defaults.object_class->byval_arg;
-
-       /* just the store right now: add an hook for the GC to use, maybe something
-        * that can be used for stelemref as well
-        * We need a write barrier variant to be used with struct copies as well, though
-        * there are also other approaches possible, like writing a wrapper specific to
-        * the struct or to the reference pattern in the struct...
-        * Depending on the GC, we may want variants that take the object we store to
-        * when it is available.
-        */
-       mono_mb_emit_ldarg (mb, 0);
-       mono_mb_emit_ldarg (mb, 1);
-       mono_mb_emit_icall (mb, mono_gc_wbarrier_generic_store);
-       /*mono_mb_emit_byte (mb, CEE_STIND_REF);*/
-
-       mono_mb_emit_byte (mb, CEE_RET);
-
-       ret = mono_mb_create_method (mb, sig, max_stack);
-       mono_mb_free (mb);
-       return ret;
-}
-
 void*
 mono_marshal_alloc (gulong size)
 {
@@ -10744,6 +10734,8 @@ ves_icall_System_Runtime_InteropServices_Marshal_GetIUnknownForObjectInternal (M
        if (!object)
                return NULL;
 
+       mono_init_com_types ();
+
        if (cominterop_object_is_rcw (object)) {
                MonoClass *klass = NULL;
                MonoRealProxy* real_proxy = NULL;
@@ -10804,6 +10796,8 @@ void*
 ves_icall_System_Runtime_InteropServices_Marshal_GetIDispatchForObjectInternal (MonoObject* object)
 {
 #ifndef DISABLE_COM
+       mono_init_com_types ();
+
        return cominterop_get_idispatch_for_object (object);
 #else
        g_assert_not_reached ();
@@ -11547,6 +11541,8 @@ mono_marshal_load_type_info (MonoClass* klass)
                info->native_size &= ~(min_align - 1);
        }
 
+       info->min_align = min_align;
+
        /* Update the class's blittable info, if the layouts don't match */
        if (info->native_size != mono_class_value_size (klass, NULL))
                klass->blittable = FALSE;
@@ -11591,7 +11587,7 @@ mono_class_native_size (MonoClass *klass, guint32 *align)
        }
 
        if (align)
-               *align = klass->min_align;
+               *align = klass->marshal_info->min_align;
 
        return klass->marshal_info->native_size;
 }
@@ -11931,10 +11927,10 @@ mono_marshal_get_generic_array_helper (MonoClass *class, MonoClass *iface, gchar
        mono_mb_emit_managed_call (mb, method, NULL);
        mono_mb_emit_byte (mb, CEE_RET);
 
-       res = mono_mb_create_method (mb, csig, csig->param_count + 16);
-
        /* We can corlib internal methods */
-       res->skip_visibility = TRUE;
+       mb->skip_visibility = TRUE;
+
+       res = mono_mb_create_method (mb, csig, csig->param_count + 16);
 
        mono_mb_free (mb);
 
@@ -12222,6 +12218,9 @@ cominterop_get_ccw (MonoObject* object, MonoClass* itf)
                                mspecs [0] = NULL;
                        }
 
+                       /* skip visiblity since we call internal methods */
+                       mb->skip_visibility = TRUE;
+
                        cominterop_setup_marshal_context (&m, adjust_method);
                        m.mb = mb;
                        mono_marshal_emit_managed_wrapper (mb, sig_adjusted, mspecs, &m, adjust_method, NULL);
@@ -12231,9 +12230,6 @@ cominterop_get_ccw (MonoObject* object, MonoClass* itf)
                        mono_marshal_unlock ();
                        mono_loader_unlock ();
 
-                       /* skip visiblity since we call internal methods */
-                       wrapper_method->skip_visibility = TRUE;
-
                        vtable [vtable_index--] = mono_compile_method (wrapper_method);
 
                        
@@ -12330,7 +12326,6 @@ cominterop_get_managed_wrapper_adjusted (MonoMethod *method)
        MonoMarshalSpec **mspecs;
        MonoMethodSignature *sig, *sig_native;
        MonoExceptionClause *main_clause = NULL;
-       MonoMethodHeader *header;
        int pos_leave;
        int hr = 0;
        int i;
@@ -12405,6 +12400,8 @@ cominterop_get_managed_wrapper_adjusted (MonoMethod *method)
                main_clause->handler_len = mono_mb_get_pos (mb) - main_clause->handler_offset;
                /* end catch */
 
+               mono_mb_set_clauses (mb, 1, main_clause);
+
                mono_mb_patch_branch (mb, pos_leave);
 
                mono_mb_emit_ldloc (mb, hr);
@@ -12425,12 +12422,6 @@ cominterop_get_managed_wrapper_adjusted (MonoMethod *method)
                        mono_metadata_free_marshal_spec (mspecs [i]);
        g_free (mspecs);
 
-       if (!preserve_sig) {
-               header = ((MonoMethodNormal *)res)->header;
-               header->num_clauses = 1;
-               header->clauses = main_clause;
-       }
-
        return res;
 }
 
@@ -12664,7 +12655,6 @@ mono_marshal_get_thunk_invoke_wrapper (MonoMethod *method)
        MonoMethodBuilder *mb;
        MonoMethodSignature *sig, *csig;
        MonoExceptionClause *clause;
-       MonoMethodHeader *header;
        MonoImage *image;
        MonoClass *klass;
        GHashTable *cache;
@@ -12783,6 +12773,8 @@ mono_marshal_get_thunk_invoke_wrapper (MonoMethod *method)
 
        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 */
 
@@ -12799,9 +12791,25 @@ mono_marshal_get_thunk_invoke_wrapper (MonoMethod *method)
        res = mono_mb_create_and_cache (cache, method, mb, csig, param_count + 16);
        mono_mb_free (mb);
 
-       header = ((MonoMethodNormal *)res)->header;
-       header->num_clauses = 1;
-       header->clauses = clause;
-
        return res;
 }
+
+/*
+ * mono_marshal_free_dynamic_wrappers:
+ *
+ *   Free wrappers of the dynamic method METHOD.
+ */
+void
+mono_marshal_free_dynamic_wrappers (MonoMethod *method)
+{
+       g_assert (method->dynamic);
+
+       mono_marshal_lock ();
+       /* 
+        * FIXME: We currently leak the wrappers. Freeing them would be tricky as
+        * they could be shared with other methods ?
+        */
+       if (method->klass->image->runtime_invoke_direct_cache)
+               g_hash_table_remove (method->klass->image->runtime_invoke_direct_cache, method);
+       mono_marshal_unlock ();
+}