From 2e68ecceafad0437cda692c38f1717837d8e31b7 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Mon, 13 Dec 2021 01:30:58 +0600 Subject: [PATCH 1/4] Add custom marshaller tests for delegates These tests added to better test ICustomMarshaller support in NativeAOT as per discussed with @jkotas --- src/tests/Interop/CMakeLists.txt | 1 + .../Primitives/CMakeLists.txt | 10 ++++ .../Primitives/ICustomMarshaler.cs | 59 ++++++++++++++++++- .../Primitives/ICustomMarshalerNative.cpp | 33 +++++++++++ .../ICustomMarshaler_TargetUnix.csproj | 3 + .../ICustomMarshaler_TargetWindows.csproj | 3 + 6 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 src/tests/Interop/ICustomMarshaler/Primitives/CMakeLists.txt create mode 100644 src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshalerNative.cpp diff --git a/src/tests/Interop/CMakeLists.txt b/src/tests/Interop/CMakeLists.txt index 1914910027eb47..35f34f87d6b49c 100644 --- a/src/tests/Interop/CMakeLists.txt +++ b/src/tests/Interop/CMakeLists.txt @@ -57,6 +57,7 @@ add_subdirectory(NativeLibrary/NativeLibraryToLoad) add_subdirectory(DllImportAttribute/DllImportPath) add_subdirectory(DllImportAttribute/ExactSpelling) add_subdirectory(ICustomMarshaler/ConflictingNames) +add_subdirectory(ICustomMarshaler/Primitives) add_subdirectory(LayoutClass) add_subdirectory(PInvoke/DateTime) if(CLR_CMAKE_TARGET_WIN32) diff --git a/src/tests/Interop/ICustomMarshaler/Primitives/CMakeLists.txt b/src/tests/Interop/ICustomMarshaler/Primitives/CMakeLists.txt new file mode 100644 index 00000000000000..1264ebcf0a2252 --- /dev/null +++ b/src/tests/Interop/ICustomMarshaler/Primitives/CMakeLists.txt @@ -0,0 +1,10 @@ +project (CustomMarshalersPrimitives) +include_directories(${INC_PLATFORM_DIR}) +set(SOURCES ICustomMarshalerNative.cpp ) + +# add the executable +add_library (CustomMarshalersPrimitives SHARED ${SOURCES}) +target_link_libraries(CustomMarshalersPrimitives ${LINK_LIBRARIES_ADDITIONAL}) + +# add the install targets +install (TARGETS CustomMarshalersPrimitives DESTINATION bin) diff --git a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs index a8ef865e7a832b..4a360936ce01be 100644 --- a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs +++ b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs @@ -164,6 +164,37 @@ public static void Parameter_CustomMarshalerProvided_CallsMethodsInCorrectOrderi "Called CleanUpNativeData" }); Assert.Equal(expectedOrderingSecondCall, OrderTrackingCustomMarshaler.Events); + + // GetInstance is only called once. + string val3 = "7488"; + Assert.Equal(val3, OrderTrackingMethodRef(ref val3)); + IEnumerable expectedOrderingThirdCall = expectedOrderingSecondCall.Concat(new string[] + { + "Called MarshalManagedToNative", + "Called MarshalNativeToManaged", + "Called CleanUpManagedData", + "Called MarshalNativeToManaged", + "Called CleanUpNativeData", + }); + Assert.Equal(expectedOrderingThirdCall.Skip(7), OrderTrackingCustomMarshaler.Events.Skip(7)); + + OrderTrackingMethodOut(out var val4); + Assert.Equal("2334", val4); + IEnumerable expectedOrderingForthCall = expectedOrderingThirdCall.Concat(new string[] + { + "Called MarshalNativeToManaged", + }); + Assert.Equal(expectedOrderingForthCall.Skip(12), OrderTrackingCustomMarshaler.Events.Skip(12)); + + var val5 = OrderTrackingMethodDelegate(439, (x) => x.ToString()); + Assert.Equal("439", val5); + IEnumerable expectedOrderingFifthCall = expectedOrderingForthCall.Concat(new string[] + { + "Called MarshalManagedToNative", + "Called CleanUpManagedData", + "Called MarshalNativeToManaged", + }); + Assert.Equal(expectedOrderingFifthCall.Skip(13), OrderTrackingCustomMarshaler.Events.Skip(13)); } // This should only be used *once*, as it uses static state. @@ -201,7 +232,7 @@ public IntPtr MarshalManagedToNative(object ManagedObj) public object MarshalNativeToManaged(IntPtr pNativeData) { Events.Add("Called MarshalNativeToManaged"); - return pNativeData.ToInt32().ToString(); + return pNativeData.ToInt64().ToString(); } public static ICustomMarshaler GetInstance(string cookie) @@ -216,6 +247,20 @@ public static ICustomMarshaler GetInstance(string cookie) [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] public static extern string OrderTrackingMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] string str); + [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntRef", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] + public static extern string OrderTrackingMethodRef([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] ref string str); + + [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntOut", CallingConvention = CallingConvention.Cdecl)] + public static extern void OrderTrackingMethodOut([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] out string str); + + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] + public delegate string TestDelegate(int val); + + [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntDelegate", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] + public static extern string OrderTrackingMethodDelegate(int val, TestDelegate dlg); + public static void CustomMarshaler_BothMarshalTypeRefAndMarshalTypeProvided_PicksMarshalType() { Assert.Equal(2, BothTypeRefAndTypeMethod("64001")); @@ -613,6 +658,17 @@ public void CleanUpManagedData(object ManagedObj) { } [DllImport(LibcLibrary, EntryPoint = "atoi", CallingConvention = CallingConvention.Cdecl)] public static extern int DifferentCustomMarshalerType([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OuterCustomMarshaler))] string str); + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BoxedValueTypeCustomMarshaler))] + public delegate string TestDelegateRef([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BoxedValueTypeCustomMarshaler))] ref int val); + + [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntDelegateRef", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BoxedValueTypeCustomMarshaler))] + public static extern string CustomMarshallerWithDelegateRef(int val, TestDelegateRef dlg); + + public static void DelegateParameter_MarshalerOnRefInt_ThrowsMarshalDirectiveException() + { + Assert.Throws(() => CustomMarshallerWithDelegateRef(84664, (ref int x) => x.ToString())); + } public static int Main(String[] args) { try @@ -643,6 +699,7 @@ public static int Main(String[] args) Parameter_CleanUpNativeDataMethodThrows_ThrowsActualException(); Field_ParentIsStruct_ThrowsTypeLoadException(); Parameter_DifferentCustomMarshalerType_MarshalsCorrectly(); + DelegateParameter_MarshalerOnRefInt_ThrowsMarshalDirectiveException(); } catch (Exception e) { diff --git a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshalerNative.cpp b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshalerNative.cpp new file mode 100644 index 00000000000000..800cf1ad099869 --- /dev/null +++ b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshalerNative.cpp @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include + +using TestDelegate = LPCSTR(STDMETHODCALLTYPE*)(int); +using TestDelegateRef = LPCSTR(STDMETHODCALLTYPE*)(int*); + +extern "C" int DLL_EXPORT STDMETHODCALLTYPE NativeParseInt(LPCSTR str) +{ + return atoi(str); +} + +extern "C" int DLL_EXPORT STDMETHODCALLTYPE NativeParseIntRef(LPCSTR* str) +{ + return atoi(*str); +} + +extern "C" void DLL_EXPORT STDMETHODCALLTYPE NativeParseIntOut(int* outVal) +{ + *outVal = 2334; +} + +extern "C" int DLL_EXPORT STDMETHODCALLTYPE NativeParseIntDelegate(int val, TestDelegate del) +{ + return atoi(del(val)); +} + +extern "C" int DLL_EXPORT STDMETHODCALLTYPE NativeParseIntDelegateRef(int val, TestDelegateRef del) +{ + return atoi(del(&val)); +} diff --git a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetUnix.csproj b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetUnix.csproj index 6fcc5f2e6ac204..d6ea6816acbb12 100644 --- a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetUnix.csproj +++ b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetUnix.csproj @@ -10,4 +10,7 @@ + + + diff --git a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetWindows.csproj b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetWindows.csproj index febed8fcc2aa01..44a0c80b382281 100644 --- a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetWindows.csproj +++ b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler_TargetWindows.csproj @@ -11,4 +11,7 @@ + + + From d926c7fa83869ce7e43d3f3cd1e50be26965161e Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Mon, 13 Dec 2021 09:01:27 +0600 Subject: [PATCH 2/4] Change call convention Too much copy-pasta is bad for your health. --- .../ICustomMarshaler/Primitives/ICustomMarshaler.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs index 4a360936ce01be..50001a538a19a6 100644 --- a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs +++ b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs @@ -247,17 +247,17 @@ public static ICustomMarshaler GetInstance(string cookie) [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] public static extern string OrderTrackingMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] string str); - [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntRef", CallingConvention = CallingConvention.Cdecl)] + [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntRef", CallingConvention = CallingConvention.StdCall)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] public static extern string OrderTrackingMethodRef([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] ref string str); - [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntOut", CallingConvention = CallingConvention.Cdecl)] + [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntOut", CallingConvention = CallingConvention.StdCall)] public static extern void OrderTrackingMethodOut([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] out string str); [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] public delegate string TestDelegate(int val); - [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntDelegate", CallingConvention = CallingConvention.Cdecl)] + [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntDelegate", CallingConvention = CallingConvention.StdCall)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] public static extern string OrderTrackingMethodDelegate(int val, TestDelegate dlg); @@ -661,7 +661,7 @@ public void CleanUpManagedData(object ManagedObj) { } [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BoxedValueTypeCustomMarshaler))] public delegate string TestDelegateRef([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BoxedValueTypeCustomMarshaler))] ref int val); - [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntDelegateRef", CallingConvention = CallingConvention.Cdecl)] + [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntDelegateRef", CallingConvention = CallingConvention.StdCall)] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BoxedValueTypeCustomMarshaler))] public static extern string CustomMarshallerWithDelegateRef(int val, TestDelegateRef dlg); From 859c635690cf5bf106273a6f7f8f9c60813bedf5 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Tue, 14 Dec 2021 23:31:57 +0600 Subject: [PATCH 3/4] Address PR feedback --- .../ICustomMarshaler/Primitives/ICustomMarshaler.cs | 8 ++++---- .../Primitives/ICustomMarshalerNative.cpp | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs index 50001a538a19a6..9c383b70214aad 100644 --- a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs +++ b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshaler.cs @@ -247,17 +247,17 @@ public static ICustomMarshaler GetInstance(string cookie) [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] public static extern string OrderTrackingMethod([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] string str); - [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntRef", CallingConvention = CallingConvention.StdCall)] + [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntRef")] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] public static extern string OrderTrackingMethodRef([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] ref string str); - [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntOut", CallingConvention = CallingConvention.StdCall)] + [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntOut")] public static extern void OrderTrackingMethodOut([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] out string str); [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] public delegate string TestDelegate(int val); - [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntDelegate", CallingConvention = CallingConvention.StdCall)] + [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntDelegate")] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(OrderTrackingCustomMarshaler))] public static extern string OrderTrackingMethodDelegate(int val, TestDelegate dlg); @@ -661,7 +661,7 @@ public void CleanUpManagedData(object ManagedObj) { } [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BoxedValueTypeCustomMarshaler))] public delegate string TestDelegateRef([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BoxedValueTypeCustomMarshaler))] ref int val); - [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntDelegateRef", CallingConvention = CallingConvention.StdCall)] + [DllImport("CustomMarshalersPrimitives", EntryPoint = "NativeParseIntDelegateRef")] [return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(BoxedValueTypeCustomMarshaler))] public static extern string CustomMarshallerWithDelegateRef(int val, TestDelegateRef dlg); diff --git a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshalerNative.cpp b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshalerNative.cpp index 800cf1ad099869..ce1637fda75313 100644 --- a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshalerNative.cpp +++ b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshalerNative.cpp @@ -7,27 +7,27 @@ using TestDelegate = LPCSTR(STDMETHODCALLTYPE*)(int); using TestDelegateRef = LPCSTR(STDMETHODCALLTYPE*)(int*); -extern "C" int DLL_EXPORT STDMETHODCALLTYPE NativeParseInt(LPCSTR str) +extern "C" int DLL_EXPORT NativeParseInt(LPCSTR str) { return atoi(str); } -extern "C" int DLL_EXPORT STDMETHODCALLTYPE NativeParseIntRef(LPCSTR* str) +extern "C" int DLL_EXPORT NativeParseIntRef(LPCSTR* str) { return atoi(*str); } -extern "C" void DLL_EXPORT STDMETHODCALLTYPE NativeParseIntOut(int* outVal) +extern "C" void DLL_EXPORT NativeParseIntOut(int* outVal) { *outVal = 2334; } -extern "C" int DLL_EXPORT STDMETHODCALLTYPE NativeParseIntDelegate(int val, TestDelegate del) +extern "C" int DLL_EXPORT NativeParseIntDelegate(int val, TestDelegate del) { return atoi(del(val)); } -extern "C" int DLL_EXPORT STDMETHODCALLTYPE NativeParseIntDelegateRef(int val, TestDelegateRef del) +extern "C" int DLL_EXPORT NativeParseIntDelegateRef(int val, TestDelegateRef del) { return atoi(del(&val)); } From c880f36c8474215ad04225e673776b5382145a76 Mon Sep 17 00:00:00 2001 From: Andrii Kurdiumov Date: Tue, 14 Dec 2021 23:43:55 +0600 Subject: [PATCH 4/4] Probably this is what Jeremy want from me --- .../Primitives/ICustomMarshalerNative.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshalerNative.cpp b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshalerNative.cpp index ce1637fda75313..800cf1ad099869 100644 --- a/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshalerNative.cpp +++ b/src/tests/Interop/ICustomMarshaler/Primitives/ICustomMarshalerNative.cpp @@ -7,27 +7,27 @@ using TestDelegate = LPCSTR(STDMETHODCALLTYPE*)(int); using TestDelegateRef = LPCSTR(STDMETHODCALLTYPE*)(int*); -extern "C" int DLL_EXPORT NativeParseInt(LPCSTR str) +extern "C" int DLL_EXPORT STDMETHODCALLTYPE NativeParseInt(LPCSTR str) { return atoi(str); } -extern "C" int DLL_EXPORT NativeParseIntRef(LPCSTR* str) +extern "C" int DLL_EXPORT STDMETHODCALLTYPE NativeParseIntRef(LPCSTR* str) { return atoi(*str); } -extern "C" void DLL_EXPORT NativeParseIntOut(int* outVal) +extern "C" void DLL_EXPORT STDMETHODCALLTYPE NativeParseIntOut(int* outVal) { *outVal = 2334; } -extern "C" int DLL_EXPORT NativeParseIntDelegate(int val, TestDelegate del) +extern "C" int DLL_EXPORT STDMETHODCALLTYPE NativeParseIntDelegate(int val, TestDelegate del) { return atoi(del(val)); } -extern "C" int DLL_EXPORT NativeParseIntDelegateRef(int val, TestDelegateRef del) +extern "C" int DLL_EXPORT STDMETHODCALLTYPE NativeParseIntDelegateRef(int val, TestDelegateRef del) { return atoi(del(&val)); }