New test.
[mono.git] / mono / metadata / cominterop.c
index bfb0dd5bf5725927291d67119323d99e6a459d33..6d9fe24c3c1ddb829b7cc7dbf74e1329b46cfdab 100644 (file)
@@ -127,7 +127,7 @@ cominterop_get_ccw_object (MonoCCWInterface* ccw_entry, gboolean verify);
 
 /* SAFEARRAY marshalling */
 static gboolean
-mono_marshal_safearray_begin (gpointer safearray, MonoArray **result, gpointer *indices, gpointer empty);
+mono_marshal_safearray_begin (gpointer safearray, MonoArray **result, gpointer *indices, gpointer empty, gpointer parameter, gboolean allocateNewArray);
 
 static gpointer
 mono_marshal_safearray_get_value (gpointer safearray, gpointer indices);
@@ -138,6 +138,15 @@ mono_marshal_safearray_next (gpointer safearray, gpointer indices);
 static void
 mono_marshal_safearray_end (gpointer safearray, gpointer indices);
 
+static gboolean
+mono_marshal_safearray_create (MonoArray *input, gpointer *newsafearray, gpointer *indices, gpointer empty);
+
+static void
+mono_marshal_safearray_set_value (gpointer safearray, gpointer indices, gpointer value);
+
+static void
+mono_marshal_safearray_free_indices (gpointer indices);
+
 /**
  * cominterop_method_signature:
  * @method: a method
@@ -349,7 +358,7 @@ cominterop_get_com_slot_for_method (MonoMethod* method)
 
 
 static void
-cominterop_mono_string_to_guid (const MonoString* string, guint8 *guid);
+cominterop_mono_string_to_guid (MonoString* string, guint8 *guid);
 
 static gboolean
 cominterop_class_guid (MonoClass* klass, guint8* guid)
@@ -526,10 +535,13 @@ mono_cominterop_init (void)
        register_icall (cominterop_type_from_handle, "cominterop_type_from_handle", "object ptr", FALSE);
 
        /* SAFEARRAY marshalling */
-       register_icall (mono_marshal_safearray_begin, "mono_marshal_safearray_begin", "int32 ptr ptr ptr ptr", FALSE);
+       register_icall (mono_marshal_safearray_begin, "mono_marshal_safearray_begin", "int32 ptr ptr ptr ptr ptr int32", FALSE);
        register_icall (mono_marshal_safearray_get_value, "mono_marshal_safearray_get_value", "ptr ptr ptr", FALSE);
        register_icall (mono_marshal_safearray_next, "mono_marshal_safearray_next", "int32 ptr ptr", FALSE);
        register_icall (mono_marshal_safearray_end, "mono_marshal_safearray_end", "void ptr ptr", FALSE);
+       register_icall (mono_marshal_safearray_create, "mono_marshal_safearray_create", "int32 object ptr ptr ptr", FALSE);
+       register_icall (mono_marshal_safearray_set_value, "mono_marshal_safearray_set_value", "void ptr ptr ptr", FALSE);
+       register_icall (mono_marshal_safearray_free_indices, "mono_marshal_safearray_free_indices", "void ptr", FALSE);
 }
 
 void
@@ -2229,7 +2241,7 @@ cominterop_get_managed_wrapper_adjusted (MonoMethod *method)
  * to a 16 byte Microsoft GUID.
  */
 static void
-cominterop_mono_string_to_guid (const MonoString* string, guint8 *guid) {
+cominterop_mono_string_to_guid (MonoString* string, guint8 *guid) {
        gunichar2 * chars = mono_string_chars (string);
        int i = 0;
        static guint8 indexes[16] = {7, 5, 3, 1, 12, 10, 17, 15, 20, 22, 25, 27, 29, 31, 33, 35};
@@ -2433,17 +2445,31 @@ static SysAllocStringLenFunc sys_alloc_string_len_ms = NULL;
 static SysStringLenFunc sys_string_len_ms = NULL;
 static SysFreeStringFunc sys_free_string_ms = NULL;
 
+#ifndef HOST_WIN32
+
+typedef struct tagSAFEARRAYBOUND {
+       ULONG cElements;
+       LONG lLbound;
+}SAFEARRAYBOUND,*LPSAFEARRAYBOUND;
+#define VT_VARIANT 12
+
+#endif 
+
 typedef guint32 (STDCALL *SafeArrayGetDimFunc)(gpointer psa);
 typedef int (STDCALL *SafeArrayGetLBoundFunc)(gpointer psa, guint32 nDim, glong* plLbound);
 typedef int (STDCALL *SafeArrayGetUBoundFunc)(gpointer psa, guint32 nDim, glong* plUbound);
 typedef int (STDCALL *SafeArrayPtrOfIndexFunc)(gpointer psa, glong* rgIndices, gpointer* ppvData);
 typedef int (STDCALL *SafeArrayDestroyFunc)(gpointer psa);
+typedef int (STDCALL *SafeArrayPutElementFunc)(gpointer psa, glong* rgIndices, gpointer* ppvData);
+typedef gpointer (STDCALL *SafeArrayCreateFunc)(int vt, guint32 cDims, SAFEARRAYBOUND* rgsabound);
 
 static SafeArrayGetDimFunc safe_array_get_dim_ms = NULL;
 static SafeArrayGetLBoundFunc safe_array_get_lbound_ms = NULL;
 static SafeArrayGetUBoundFunc safe_array_get_ubound_ms = NULL;
 static SafeArrayPtrOfIndexFunc safe_array_ptr_of_index_ms = NULL;
 static SafeArrayDestroyFunc safe_array_destroy_ms = NULL;
+static SafeArrayPutElementFunc safe_array_put_element_ms = NULL;
+static SafeArrayCreateFunc safe_array_create_ms = NULL;
 
 static gboolean
 init_com_provider_ms (void)
@@ -2518,6 +2544,20 @@ init_com_provider_ms (void)
                return FALSE;
        }
 
+       error_msg = mono_dl_symbol (module, "SafeArrayPutElement", (gpointer*)&safe_array_put_element_ms);
+       if (error_msg) {
+               g_warning ("Error loading entry point '%s' in COM support library '%s': %s", "SafeArrayPutElement", scope, error_msg);
+               g_assert_not_reached ();
+               return FALSE;
+       }
+
+       error_msg = mono_dl_symbol (module, "SafeArrayCreate", (gpointer*)&safe_array_create_ms);
+       if (error_msg) {
+               g_warning ("Error loading entry point '%s' in COM support library '%s': %s", "SafeArrayCreate", scope, error_msg);
+               g_assert_not_reached ();
+               return FALSE;
+       }
+
        initialized = TRUE;
        return TRUE;
 }
@@ -2617,33 +2657,152 @@ mono_cominterop_emit_marshal_safearray (EmitMarshalContext *m, int argnum, MonoT
        
        switch (action) {
 
-       case MARSHAL_ACTION_CONV_OUT: {
+       case MARSHAL_ACTION_CONV_IN: {
+
+               if (t->attrs & PARAM_ATTRIBUTE_IN) {
+
+                       /* Generates IL code for the following algorithm:
+
+                                       SafeArray safearray;   // safearray_var
+                                       IntPtr indices; // indices_var
+                                       int empty;      // empty_var
+                                       if (mono_marshal_safearray_create (array, out safearray, out indices, out empty)) {
+                                               if (!empty) {
+                                                       int index=0; // index_var
+                                                       do { // label3
+                                                               variant elem = Marshal.GetNativeVariantForObject (array.GetValueImpl(index));
+                                                               mono_marshal_safearray_set_value (safearray, indices, elem);
+                                                               ++index;
+                                                       } 
+                                                       while (mono_marshal_safearray_next (safearray, indices));
+                                               } // label2
+                                               mono_marshal_safearray_free_indices (indices);
+                                       } // label1
+                       */
+
+                       int safearray_var, indices_var, empty_var, elem_var, index_var;
+                       guint32 label1 = 0, label2 = 0, label3 = 0;
+                       static MonoMethod *get_native_variant_for_object = NULL;
+                       static MonoMethod *get_value_impl = NULL;
+                       static MonoMethod *variant_clear = NULL;
+
+                       conv_arg = safearray_var = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg);
+                       indices_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+                       empty_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+
+                       if (t->byref) {
+                               mono_mb_emit_ldarg (mb, argnum);
+                               mono_mb_emit_byte (mb, CEE_LDIND_I);
+                       } else
+                               mono_mb_emit_ldarg (mb, argnum);
+
+                       mono_mb_emit_ldloc_addr (mb, safearray_var);
+                       mono_mb_emit_ldloc_addr (mb, indices_var);
+                       mono_mb_emit_ldloc_addr (mb, empty_var);
+                       mono_mb_emit_icall (mb, mono_marshal_safearray_create);
+
+                       label1 = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S);
+
+                       mono_mb_emit_ldloc (mb, empty_var);
+
+                       label2 = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S);
+
+                       index_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
+                       mono_mb_emit_byte (mb, CEE_LDC_I4_0);
+                       mono_mb_emit_stloc (mb, index_var);
+
+                       label3 = mono_mb_get_label (mb);
+
+                       if (!get_value_impl)
+                               get_value_impl = mono_class_get_method_from_name (mono_defaults.array_class, "GetValueImpl", 1);
+                       g_assert (get_value_impl);
+
+                       if (t->byref) {
+                               mono_mb_emit_ldarg (mb, argnum);
+                               mono_mb_emit_byte (mb, CEE_LDIND_I);
+                       } else
+                               mono_mb_emit_ldarg (mb, argnum);
+
+                       mono_mb_emit_ldloc (mb, index_var);
+
+                       mono_mb_emit_managed_call (mb, get_value_impl, NULL);
+
+                       if (!get_native_variant_for_object)
+                               get_native_variant_for_object = mono_class_get_method_from_name (mono_defaults.marshal_class, "GetNativeVariantForObject", 2);
+                       g_assert (get_native_variant_for_object);
+
+                       elem_var =  mono_mb_add_local (mb, &mono_defaults.variant_class->byval_arg);
+                       mono_mb_emit_ldloc_addr (mb, elem_var);
+
+                       mono_mb_emit_managed_call (mb, get_native_variant_for_object, NULL);
+
+                       mono_mb_emit_ldloc (mb, safearray_var);
+                       mono_mb_emit_ldloc (mb, indices_var);
+                       mono_mb_emit_ldloc_addr (mb, elem_var);
+                       mono_mb_emit_icall (mb, mono_marshal_safearray_set_value);
+
+                       if (!variant_clear)
+                               variant_clear = mono_class_get_method_from_name (mono_defaults.variant_class, "Clear", 0);
+
+                       mono_mb_emit_ldloc_addr (mb, elem_var);
+                       mono_mb_emit_managed_call (mb, variant_clear, NULL);
+
+                       mono_mb_emit_add_to_local (mb, index_var, 1);
+
+                       mono_mb_emit_ldloc (mb, safearray_var);
+                       mono_mb_emit_ldloc (mb, indices_var);
+                       mono_mb_emit_icall (mb, mono_marshal_safearray_next);
+                       mono_mb_emit_branch_label (mb, CEE_BRTRUE, label3);
+
+                       mono_mb_patch_short_branch (mb, label2);
+
+                       mono_mb_emit_ldloc (mb, indices_var);
+                       mono_mb_emit_icall (mb, mono_marshal_safearray_free_indices);
+
+                       mono_mb_patch_short_branch (mb, label1);
+               }
+               break;
+       }
+
+       case MARSHAL_ACTION_PUSH:
+               if (t->byref)
+                       mono_mb_emit_ldloc_addr (mb, conv_arg);
+               else
+                       mono_mb_emit_ldloc (mb, conv_arg);
+               break;
 
-               if (t->byref && (t->attrs & PARAM_ATTRIBUTE_OUT || !(t->attrs & PARAM_ATTRIBUTE_IN))) {
+       case MARSHAL_ACTION_CONV_OUT: {
 
+               if (t->attrs & PARAM_ATTRIBUTE_OUT) {
                        /* Generates IL code for the following algorithm:
 
                                        Array result;   // result_var
                                        IntPtr indices; // indices_var
                                        int empty;      // empty_var
-                                       if (mono_marshal_safearray_begin(safearray, out result, out indices, out empty)) {
+                                       bool byValue = !t->byref && (t->attrs & PARAM_ATTRIBUTE_IN);
+                                       if (mono_marshal_safearray_begin(safearray, out result, out indices, out empty, parameter, byValue)) {
                                                if (!empty) {
                                                        int index=0; // index_var
                                                        do { // label3
-                                                               object elem = Variant.GetObjectForNativeVariant(mono_marshal_safearray_get_value(safearray, indices));
-                                                               result.SetValueImpl(elem, index);
+                                                               if (!byValue || (index < parameter.Length)) {
+                                                                       object elem = Variant.GetObjectForNativeVariant(mono_marshal_safearray_get_value(safearray, indices));
+                                                                       result.SetValueImpl(elem, index);
+                                                               }
                                                                ++index;
                                                        } 
                                                        while (mono_marshal_safearray_next(safearray, indices));
                                                } // label2
                                                mono_marshal_safearray_end(safearray, indices);
                                        } // label1
+                                       if (!byValue)
+                                               return result;
                        */
 
                        int result_var, indices_var, empty_var, elem_var, index_var;
-                       guint32 label1 = 0, label2 = 0, label3 = 0;
+                       guint32 label1 = 0, label2 = 0, label3 = 0, label4 = 0;
                        static MonoMethod *get_object_for_native_variant = NULL;
                        static MonoMethod *set_value_impl = NULL;
+                       gboolean byValue = !t->byref && (t->attrs & PARAM_ATTRIBUTE_IN);
 
                        result_var = mono_mb_add_local (mb, &mono_defaults.object_class->byval_arg);
                        indices_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
@@ -2653,6 +2812,11 @@ mono_cominterop_emit_marshal_safearray (EmitMarshalContext *m, int argnum, MonoT
                        mono_mb_emit_ldloc_addr (mb, result_var);
                        mono_mb_emit_ldloc_addr (mb, indices_var);
                        mono_mb_emit_ldloc_addr (mb, empty_var);
+                       mono_mb_emit_ldarg (mb, argnum);
+                       if (byValue)
+                               mono_mb_emit_byte (mb, CEE_LDC_I4_0);
+                       else
+                               mono_mb_emit_byte (mb, CEE_LDC_I4_1);
                        mono_mb_emit_icall (mb, mono_marshal_safearray_begin);
 
                        label1 = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S);
@@ -2667,6 +2831,13 @@ mono_cominterop_emit_marshal_safearray (EmitMarshalContext *m, int argnum, MonoT
 
                        label3 = mono_mb_get_label (mb);
 
+                       if (byValue) {
+                               mono_mb_emit_ldloc (mb, index_var);
+                               mono_mb_emit_ldarg (mb, argnum);
+                               mono_mb_emit_byte (mb, CEE_LDLEN);
+                               label4 = mono_mb_emit_branch (mb, CEE_BGE);
+                       }
+
                        mono_mb_emit_ldloc (mb, conv_arg);
                        mono_mb_emit_ldloc (mb, indices_var);
                        mono_mb_emit_icall (mb, mono_marshal_safearray_get_value);
@@ -2689,6 +2860,9 @@ mono_cominterop_emit_marshal_safearray (EmitMarshalContext *m, int argnum, MonoT
                        mono_mb_emit_ldloc (mb, index_var);
                        mono_mb_emit_managed_call (mb, set_value_impl, NULL);
 
+                       if (byValue)
+                               mono_mb_patch_short_branch (mb, label4);
+
                        mono_mb_emit_add_to_local (mb, index_var, 1);
 
                        mono_mb_emit_ldloc (mb, conv_arg);
@@ -2704,9 +2878,11 @@ mono_cominterop_emit_marshal_safearray (EmitMarshalContext *m, int argnum, MonoT
 
                        mono_mb_patch_short_branch (mb, label1);
 
-                       mono_mb_emit_ldarg (mb, argnum);
-                       mono_mb_emit_ldloc (mb, result_var);
-                       mono_mb_emit_byte (mb, CEE_STIND_REF);
+                       if (!byValue) {
+                               mono_mb_emit_ldarg (mb, argnum);
+                               mono_mb_emit_ldloc (mb, result_var);
+                               mono_mb_emit_byte (mb, CEE_STIND_REF);
+                       }
                }
                break;
        }
@@ -2767,11 +2943,11 @@ int mono_marshal_safe_array_get_ubound (gpointer psa, guint nDim, glong* plUboun
 }
 
 static gboolean
-mono_marshal_safearray_begin (gpointer safearray, MonoArray **result, gpointer *indices, gpointer empty)
+mono_marshal_safearray_begin (gpointer safearray, MonoArray **result, gpointer *indices, gpointer empty, gpointer parameter, gboolean allocateNewArray)
 {
        int dim;
-       mono_array_size_t *sizes;
-       mono_array_size_t *bounds;
+       uintptr_t *sizes;
+       intptr_t *bounds;
        MonoClass *aklass;
        int i;
        gboolean bounded = FALSE;
@@ -2787,42 +2963,52 @@ mono_marshal_safearray_begin (gpointer safearray, MonoArray **result, gpointer *
        }
 #endif
 
-       dim = mono_marshal_safearray_get_dim (safearray);
+       (*(int*)empty) = TRUE;
 
-       *indices = g_malloc (dim * sizeof(int));
+       if (safearray != NULL) {
 
-       sizes = alloca (dim * sizeof(mono_array_size_t));
-       bounds = alloca (dim * sizeof(mono_array_size_t));
+               dim = mono_marshal_safearray_get_dim (safearray);
 
-       (*(int*)empty) = TRUE;
-       for (i=0; i<dim; ++i) {
-               glong lbound, ubound;
-               int cursize;
-               int hr;
+               if (dim > 0) {
 
-               hr = mono_marshal_safe_array_get_lbound (safearray, i+1, &lbound);
-               if (hr < 0) {
-                       cominterop_raise_hr_exception (hr);
-               }
-               if (lbound != 0)
-                       bounded = TRUE;
-               hr = mono_marshal_safe_array_get_ubound (safearray, i+1, &ubound);
-               if (hr < 0) {
-                       cominterop_raise_hr_exception (hr);
-               }
-               cursize = ubound-lbound+1;
-               sizes [i] = cursize;
-               bounds [i] = lbound;
+                       *indices = g_malloc (dim * sizeof(int));
 
-               ((int*)*indices) [i] = lbound;
+                       sizes = alloca (dim * sizeof(uintptr_t));
+                       bounds = alloca (dim * sizeof(intptr_t));
 
-               if (cursize != 0)
-                       (*(int*)empty) = FALSE;
-       }
+                       for (i=0; i<dim; ++i) {
+                               glong lbound, ubound;
+                               int cursize;
+                               int hr;
+
+                               hr = mono_marshal_safe_array_get_lbound (safearray, i+1, &lbound);
+                               if (hr < 0) {
+                                       cominterop_raise_hr_exception (hr);
+                               }
+                               if (lbound != 0)
+                                       bounded = TRUE;
+                               hr = mono_marshal_safe_array_get_ubound (safearray, i+1, &ubound);
+                               if (hr < 0) {
+                                       cominterop_raise_hr_exception (hr);
+                               }
+                               cursize = ubound-lbound+1;
+                               sizes [i] = cursize;
+                               bounds [i] = lbound;
 
-       aklass = mono_bounded_array_class_get (mono_defaults.object_class, dim, bounded);
-       *result = mono_array_new_full (mono_domain_get (), aklass, sizes, bounds);
+                               ((int*)*indices) [i] = lbound;
 
+                               if (cursize != 0)
+                                       (*(int*)empty) = FALSE;
+                       }
+
+                       if (allocateNewArray) {
+                               aklass = mono_bounded_array_class_get (mono_defaults.object_class, dim, bounded);
+                               *result = mono_array_new_full (mono_domain_get (), aklass, sizes, bounds);
+                       } else {
+                               *result = parameter;
+                       }
+               }
+       }
        return TRUE;
 }
 
@@ -2898,6 +3084,76 @@ void mono_marshal_safearray_end (gpointer safearray, gpointer indices)
 #endif
 }
 
+static gboolean
+mono_marshal_safearray_create (MonoArray *input, gpointer *newsafearray, gpointer *indices, gpointer empty)
+{
+       int dim;
+       SAFEARRAYBOUND *bounds;
+       int i;
+       int max_array_length;
+
+#ifndef HOST_WIN32
+       // If not on windows, check that the MS provider is used as it is 
+       // required for SAFEARRAY support.
+       // If SAFEARRAYs are not supported, returning FALSE from this
+       // function will prevent the other mono_marshal_safearray_xxx functions
+       // from being called.
+       if ((com_provider != MONO_COM_MS) || !init_com_provider_ms ()) {
+               return FALSE;
+       }
+#endif
+
+       max_array_length = mono_array_length (input);
+       dim = ((MonoObject *)input)->vtable->klass->rank;
+
+       *indices = g_malloc (dim * sizeof (int));
+       bounds = alloca (dim * sizeof (SAFEARRAYBOUND));
+       (*(int*)empty) = (max_array_length == 0);
+
+       if (dim > 1) {
+               for (i=0; i<dim; ++i) {
+                       ((int*)*indices) [i] = bounds [i].lLbound = input->bounds [i].lower_bound;
+                       bounds [i].cElements = input->bounds [i].length;
+               }
+       } else {
+               ((int*)*indices) [0] = 0;
+               bounds [0].cElements = max_array_length;
+               bounds [0].lLbound = 0;
+       }
+
+#ifdef HOST_WIN32
+       *newsafearray = SafeArrayCreate (VT_VARIANT, dim, bounds);
+#else
+       *newsafearray = safe_array_create_ms (VT_VARIANT, dim, bounds);
+#endif
+
+       return TRUE;
+}
+
+static 
+void mono_marshal_safearray_set_value (gpointer safearray, gpointer indices, gpointer value)
+{
+#ifdef HOST_WIN32
+       int hr = SafeArrayPutElement (safearray, indices, value);
+       if (hr < 0)
+               cominterop_raise_hr_exception (hr);
+#else
+       if (com_provider == MONO_COM_MS && init_com_provider_ms ()) {
+               int hr = safe_array_put_element_ms (safearray, indices, value);
+               if (hr < 0) {
+                       cominterop_raise_hr_exception (hr);
+               }
+       } else
+               g_assert_not_reached ();
+#endif
+}
+
+static 
+void mono_marshal_safearray_free_indices (gpointer indices)
+{
+       g_free (indices);
+}
+
 #else /* DISABLE_COM */
 
 void