Fix marshalling a pinnable multi-dimensional array via a P/Inv… (dotnet/coreclr#26279)
authorJeremy Koritzinsky <jekoritz@microsoft.com>
Tue, 27 Aug 2019 22:13:03 +0000 (15:13 -0700)
committerGitHub <noreply@github.com>
Tue, 27 Aug 2019 22:13:03 +0000 (15:13 -0700)
* Use the exact methodtable of the parameter to calculate the offset into a pinned array.

* Fix logic check.

* Update precondition.

* Fix accidental union overlap.

Commit migrated from https://github.com/dotnet/coreclr/commit/f43185b07889fabb2e07c8fe4c1dfcd74f17747c

src/coreclr/src/vm/ilmarshalers.cpp
src/coreclr/src/vm/mlinfo.cpp
src/coreclr/src/vm/mlinfo.h
src/coreclr/tests/src/Interop/PInvoke/Array/MarshalArrayAsParam/AsDefault/AsDefaultTest.cs
src/coreclr/tests/src/Interop/PInvoke/Array/MarshalArrayAsParam/LPArrayNative/MarshalArrayLPArrayNative.cpp

index 0fb2f71..a384f4a 100644 (file)
@@ -3701,7 +3701,9 @@ void ILNativeArrayMarshaler::EmitCreateMngdMarshaler(ILCodeStream* pslILEmit)
 
 bool ILNativeArrayMarshaler::CanMarshalViaPinning()
 {
-    return IsCLRToNative(m_dwMarshalFlags) && !IsByref(m_dwMarshalFlags) && (NULL == OleVariant::GetMarshalerForVarType(m_pargs->na.m_vt, TRUE));
+    // We can't pin an array if we have a marshaler for the var type
+    // or if we can't get a method-table representing the array (how we determine the offset to pin).
+    return IsCLRToNative(m_dwMarshalFlags) && !IsByref(m_dwMarshalFlags) && (NULL != m_pargs->na.m_pArrayMT) && (NULL == OleVariant::GetMarshalerForVarType(m_pargs->na.m_vt, TRUE));
 }
 
 void ILNativeArrayMarshaler::EmitMarshalViaPinning(ILCodeStream* pslILEmit)
@@ -3709,7 +3711,7 @@ void ILNativeArrayMarshaler::EmitMarshalViaPinning(ILCodeStream* pslILEmit)
     CONTRACTL
     {
         STANDARD_VM_CHECK;
-        PRECONDITION(IsCLRToNative(m_dwMarshalFlags) && !IsByref(m_dwMarshalFlags));
+        PRECONDITION(CanMarshalViaPinning());
     }
     CONTRACTL_END;
 
@@ -3744,7 +3746,7 @@ void ILNativeArrayMarshaler::EmitMarshalViaPinning(ILCodeStream* pslILEmit)
     pslILEmit->EmitCONV_I();
     // Optimize marshalling by emitting the data ptr offset directly into the IL stream
     // instead of doing an FCall to recalulate it each time when possible.
-    pslILEmit->EmitLDC(ArrayBase::GetDataPtrOffset(m_pargs->m_pMarshalInfo->GetArrayElementTypeHandle().MakeSZArray().GetMethodTable()));
+    pslILEmit->EmitLDC(ArrayBase::GetDataPtrOffset(m_pargs->na.m_pArrayMT));
     pslILEmit->EmitADD();
     EmitStoreNativeValue(pslILEmit);
 
index 607463a..db6f01a 100644 (file)
@@ -2802,6 +2802,8 @@ MarshalInfo::MarshalInfo(Module* pModule,
                 }
             }
 
+            m_args.na.m_pArrayMT = arrayTypeHnd.GetMethodTable();
+
             // Handle retrieving the information for the array type.
             IfFailGoto(HandleArrayElemType(&ParamInfo, thElement, asArray->GetRank(), mtype == ELEMENT_TYPE_SZARRAY, isParam, pAssembly), lFail);
             break;
index 46057d5..46dd79c 100644 (file)
@@ -82,6 +82,7 @@ struct OverrideProcArgs
 
         struct
         {
+            MethodTable*    m_pArrayMT;
             VARTYPE         m_vt;
 #ifdef FEATURE_COMINTEROP
             SIZE_T          m_cbElementSize;
index 5cdeea5..3fc4e54 100644 (file)
@@ -244,12 +244,19 @@ public class ArrayMarshal
     [DllImport("MarshalArrayLPArrayNative")]
     private static extern bool CStyle_Array_Bool_Out(
         [Out] bool[] actual, int cActual);
+
+    [DllImport("MarshalArrayLPArrayNative")]
+    private static extern int Get_Multidimensional_Array_Sum(int[,] array, int rows, int columns);
     #endregion
 
     #region Marshal ByVal
 
     private const int ARRAY_SIZE = 100;
 
+    private const int ROWS = 3;
+
+    private const int COLUMNS = 2;
+
     private static T[] InitArray<T>(int size)
     {
         T[] array = new T[size];
@@ -290,6 +297,20 @@ public class ArrayMarshal
         return array;
     }
 
+    private static int[,] InitMultidimensionalBlittableArray(int rows, int columns)
+    {
+        int[,] array = new int[rows, columns];
+
+        for (int i = 0; i < array.GetLength(0); i++)
+        {
+            for (int j = 0; j < array.GetLength(1); j++)
+            {
+                array[i, j] = i * j;
+            }
+        }
+        return array;
+    }
+
     private static void TestMarshalByVal_NoAttributes()
     {
         Console.WriteLine("ByVal marshaling CLR array as c-style-array no attributes");
@@ -619,6 +640,19 @@ public class ArrayMarshal
 
     #endregion
 
+    private static void TestMultidimensional()
+    {
+        Console.WriteLine("================== [Get_Multidimensional_Array_Sum] ============");
+        int[,] array = InitMultidimensionalBlittableArray(ROWS, COLUMNS);
+        int sum = 0;
+        foreach (int item in array)
+        {
+            sum += item;
+        }
+
+        Assert.AreEqual(sum, Get_Multidimensional_Array_Sum(array, ROWS, COLUMNS));
+    }
+
     public static int Main()
     {
         try
@@ -627,6 +661,7 @@ public class ArrayMarshal
             TestMarshalByVal_In();
             TestMarshalInOut_ByVal();
             TestMarshalOut_ByVal();
+            TestMultidimensional();
             
             Console.WriteLine("\nTest PASS.");
             return 100;
index fea447c..5f29b23 100644 (file)
@@ -1111,3 +1111,16 @@ extern "C" DLL_EXPORT BOOL MarshalArrayOfStructAsLPArrayByRefOut(S2 **pActual, i
     }
     return breturn;
 }
+
+extern "C" DLL_EXPORT int Get_Multidimensional_Array_Sum(int* array, int rows, int columns)
+{
+    int sum = 0;
+    for(int i = 0; i < rows; ++i)
+    {
+        for (int j = 0; j < columns; ++j)
+        {
+            sum += array[i * columns + j];
+        }
+    }
+    return sum;
+}