[cominterop] Implement native-to-managed safearray marshalling. (mono/mono#16483)
authorNikolay Sivov <nsivov@codeweavers.com>
Thu, 29 Aug 2019 12:19:09 +0000 (15:19 +0300)
committerZoltan Varga <vargaz@gmail.com>
Thu, 29 Aug 2019 12:19:08 +0000 (08:19 -0400)
* [cominterop] Implement native-to-managed safearray marshalling.

* [tests] Fix index variable type.

In attempt ot fix MSVC build failure (int* vs LONG* on Windows).

* [cominterop] Use proper statement block for newly added switch entry.

* [marshal] Conditionally compile in mono_cominterop_emit_marshal_safearray().

Hopefully should fix remaining build failures.

* [cominterop] Don't free user array argument

Commit migrated from https://github.com/mono/mono/commit/421bd79f06a92372e0259be5349e4236edf0e7bb

src/mono/mono/metadata/cominterop.c
src/mono/mono/metadata/marshal-ilgen.c
src/mono/mono/tests/cominterop.cs
src/mono/mono/tests/libtest.c

index 3ea0eeb..2593947 100644 (file)
@@ -3220,7 +3220,108 @@ mono_cominterop_emit_marshal_safearray (EmitMarshalContext *m, int argnum, MonoT
                }
                break;
        }
+       case MARSHAL_ACTION_MANAGED_CONV_IN: {
+               if (!(t->attrs & PARAM_ATTRIBUTE_IN))
+                       break;
+
+               /* 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, NULL, TRUE)) {
+                                       if (!empty) {
+                                               int index=0; // index_var
+                                               do { // label3
+                                                       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_free_indices(indices);
+                               } // label1
+               */
+
+               int result_var, indices_var, empty_var, elem_var, index_var;
+               guint32 label1 = 0, label2 = 0, label3 = 0;
+               static MonoMethod *get_object_for_native_variant = NULL;
+               static MonoMethod *set_value_impl = NULL;
+
+               MonoType *object_type = mono_get_object_type ();
+               MonoType *int_type = mono_get_int_type ();
+               result_var = mono_mb_add_local (mb, object_type);
+               indices_var = mono_mb_add_local (mb, int_type);
+               empty_var = mono_mb_add_local (mb, int_type);
+
+               mono_mb_emit_ldarg (mb, argnum);
+               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_byte (mb, CEE_LDC_I4_0);
+               mono_mb_emit_byte (mb, CEE_CONV_I);
+               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);
 
+               mono_mb_emit_ldloc (mb, empty_var);
+
+               label2 = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S);
+
+               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);
+
+               label3 = mono_mb_get_label (mb);
+
+               mono_mb_emit_ldarg (mb, argnum);
+               mono_mb_emit_ldloc (mb, indices_var);
+               mono_mb_emit_icall (mb, mono_marshal_safearray_get_value);
+
+               if (!get_object_for_native_variant) {
+                       ERROR_DECL (error);
+                       get_object_for_native_variant = mono_class_get_method_from_name_checked (mono_defaults.marshal_class, "GetObjectForNativeVariant", 1, 0, error);
+                       mono_error_assert_ok (error);
+               }
+               g_assert (get_object_for_native_variant);
+
+               if (!set_value_impl) {
+                       ERROR_DECL (error);
+                       set_value_impl = mono_class_get_method_from_name_checked (mono_defaults.array_class, "SetValueImpl", 2, 0, error);
+                       mono_error_assert_ok (error);
+               }
+               g_assert (set_value_impl);
+
+               elem_var = mono_mb_add_local (mb, object_type);
+
+               mono_mb_emit_managed_call (mb, get_object_for_native_variant, NULL);
+               mono_mb_emit_stloc (mb, elem_var);
+
+               mono_mb_emit_ldloc (mb, result_var);
+               mono_mb_emit_ldloc (mb, elem_var);
+               mono_mb_emit_ldloc (mb, index_var);
+               mono_mb_emit_managed_call (mb, set_value_impl, NULL);
+
+               mono_mb_emit_add_to_local (mb, index_var, 1);
+
+               mono_mb_emit_ldarg (mb, argnum);
+               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);
+
+               mono_mb_emit_ldloc (mb, result_var);
+               mono_mb_emit_stloc (mb, conv_arg);
+
+               break;
+       }
        default:
                g_assert_not_reached ();
        }
index 809481d..0f126a0 100644 (file)
@@ -2577,11 +2577,25 @@ emit_marshal_array_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,
                        char *msg = g_strdup ("[MarshalAs] attribute required to marshal arrays to managed code.");
                        mono_mb_emit_exception_marshal_directive (mb, msg);
                        return conv_arg;
-               }                       
-               if (spec->native != MONO_NATIVE_LPARRAY) {
-                       char *msg = g_strdup ("Non LPArray marshalling of arrays to managed code is not implemented.");
+               }
+
+               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_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_mb_emit_exception_marshal_directive (mb, msg);
-                       return conv_arg;                        
+                       return conv_arg;
+               }
                }
 
                /* FIXME: t is from the method which is wrapped, not the delegate type */
@@ -6026,7 +6040,6 @@ emit_managed_wrapper_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *invoke_s
                case MONO_TYPE_STRING:
                case MONO_TYPE_BOOLEAN:
                        tmp_locals [i] = mono_emit_marshal (m, i, sig->params [i], mspecs [i + 1], 0, &csig->params [i], MARSHAL_ACTION_MANAGED_CONV_IN);
-
                        break;
                default:
                        tmp_locals [i] = 0;
index 2afac90..6c26107 100644 (file)
@@ -322,6 +322,9 @@ public class Tests
                [In, Out, MarshalAs (UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] ref Array array4);
 
        [DllImport("libtest")]
+       public static extern int mono_test_marshal_safearray_in_ccw([MarshalAs (UnmanagedType.Interface)] ITest itest);
+
+       [DllImport("libtest")]
        public static extern bool mono_cominterop_is_supported ();
 
        public static int Main ()
@@ -606,7 +609,6 @@ public class Tests
                        if (isWindows) {
 
                                /* out */
-
                                Array array;
                                if ((mono_test_marshal_safearray_out_1dim_vt_bstr_empty (out array) != 0) || (array.Rank != 1) || (array.Length != 0))
                                        return 62;
@@ -762,6 +764,8 @@ public class Tests
                                        if (i != Convert.ToInt32 (array4.GetValue (i)))
                                                return 96;
                                }
+                               if (mono_test_marshal_safearray_in_ccw(test) != 0)
+                                       return 97;
                        }
                        #endregion // SafeArray Tests
 
@@ -817,6 +821,8 @@ public class Tests
                int Return22NoICall();
                [MethodImplAttribute (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                int IntOut();
+               [MethodImplAttribute (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
+               void ArrayIn ([In, MarshalAs (UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] object[] array);
        }
 
        [ComImport ()]
@@ -873,6 +879,8 @@ public class Tests
                [MethodImplAttribute (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                [PreserveSig ()]
                int IntOut (out int val);
+               [MethodImplAttribute (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
+               int ArrayIn ([In, MarshalAs (UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] object[] array);
        }
 
        [System.Runtime.InteropServices.GuidAttribute ("00000000-0000-0000-0000-000000000002")]
@@ -916,6 +924,8 @@ public class Tests
                public virtual extern int Return22NoICall();
                [MethodImplAttribute (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
                public virtual extern int IntOut();
+               [MethodImplAttribute (MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
+               public virtual extern void ArrayIn ([In, MarshalAs (UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] object[] array);
        }
 
        [System.Runtime.InteropServices.GuidAttribute ("00000000-0000-0000-0000-000000000002")]
@@ -1066,6 +1076,34 @@ public class Tests
                        val = 33;
                        return 0;
                }
+
+               public int ArrayIn(object[] array)
+               {
+                       if (array.Length != 2)
+                               return 40;
+                       if (array.Rank != 1)
+                               return 41;
+                       if (array.GetLowerBound(0) != 0)
+                               return 42;
+                       if (array.GetUpperBound(0) != 1)
+                               return 43;
+                       if (array[0] is string)
+                       {
+                               if ((array[0] as string) != "Test")
+                                       return 44;
+                       }
+                       else
+                               return 45;
+                       if (array[1] is int)
+                       {
+                               if ((int)array[1] != 2345)
+                                       return 46;
+                       }
+                       else
+                               return 47;
+
+                       return 444;
+               }
        }
 
        public class ManagedTest : ITest
@@ -1165,6 +1203,30 @@ public class Tests
                {
                        return 33;
                }
+
+               public void ArrayIn(object[] array)
+               {
+                       if (array.Length != 2)
+                               status = 40;
+                       else if (array.Rank != 1)
+                               status = 41;
+                       else if (array.GetLowerBound(0) != 0)
+                               status = 42;
+                       else if (array.GetUpperBound(0) != 1)
+                               status = 43;
+                       else if (array[0] is string)
+                       {
+                               if ((array[0] as string) != "Test")
+                                       status = 44;
+                       }
+                       else if (array[1] is int)
+                       {
+                               if ((int)array[1] != 2345)
+                                       status = 45;
+                       }
+
+                       status = 444;
+               }
        }
 
        [ComVisible (true)]
index d4c5ade..782edd3 100644 (file)
@@ -3390,6 +3390,7 @@ typedef struct
        int (STDCALL *ITestOut)(MonoComObject* pUnk, MonoComObject* *ppUnk);
        int (STDCALL *Return22NoICall)(MonoComObject* pUnk);
        int (STDCALL *IntOut)(MonoComObject* pUnk, int *a);
+       int (STDCALL *ArrayIn)(MonoComObject* pUnk, void *array);
 } MonoIUnknown;
 
 struct MonoComObject
@@ -3519,6 +3520,12 @@ IntOut(MonoComObject* pUnk, int *a)
        return S_OK;
 }
 
+LIBTEST_API int STDCALL
+ArrayIn(MonoComObject* pUnk, void *array)
+{
+       return S_OK;
+}
+
 static void create_com_object (MonoComObject** pOut);
 
 LIBTEST_API int STDCALL 
@@ -3552,6 +3559,7 @@ static void create_com_object (MonoComObject** pOut)
        (*pOut)->vtbl->get_ITest = get_ITest;
        (*pOut)->vtbl->Return22NoICall = Return22NoICall;
        (*pOut)->vtbl->IntOut = IntOut;
+       (*pOut)->vtbl->ArrayIn = ArrayIn;
 }
 
 static MonoComObject* same_object = NULL;
@@ -5557,6 +5565,33 @@ mono_test_marshal_safearray_mixed(
        return hr;
 }
 
+LIBTEST_API int STDCALL
+mono_test_marshal_safearray_in_ccw(MonoComObject *pUnk)
+{
+       SAFEARRAY *array;
+       VARIANT var;
+       long index;
+       int ret;
+
+       array = SafeArrayCreateVector(VT_VARIANT, 0, 2);
+
+       var.vt = VT_BSTR;
+       var.bstrVal = marshal_bstr_alloc("Test");
+       index = 0;
+       SafeArrayPutElement(array, &index, &var);
+
+       var.vt = VT_I4;
+       var.intVal = 2345;
+       index = 1;
+       SafeArrayPutElement(array, &index, &var);
+
+       ret = pUnk->vtbl->ArrayIn (pUnk, (void *)array);
+
+       SafeArrayDestroy(array);
+
+       return ret;
+}
+
 #endif
 
 static int call_managed_res;