summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorJeremy Koritzinsky <jkoritzinsky@gmail.com>2019-03-19 16:22:47 -0700
committerGitHub <noreply@github.com>2019-03-19 16:22:47 -0700
commit4bca72836811aa1b82389c50dc4f2bcae222bdc0 (patch)
tree6852f8c74ddd435a7ebc59867b00ca310a5c0c06 /tests
parentaccc6eb0f0f669494bee0788884b5a10101f1243 (diff)
downloadcoreclr-4bca72836811aa1b82389c50dc4f2bcae222bdc0.tar.gz
coreclr-4bca72836811aa1b82389c50dc4f2bcae222bdc0.tar.bz2
coreclr-4bca72836811aa1b82389c50dc4f2bcae222bdc0.zip
Implement support for copy constructors when marshalling in IJW (#22805)
Fixes #22219. I decided to try simplifying the code when bringing this feature over from .NET Framework. The .NETFX x86 implementation of this marshaler adds a lot of extra code-paths, and intercepts the calling stub to enable it to exactly replicate the behavior of what would be the native code by copy-constructing an object in-place where it goes on the stack for the native call. Instead of adding all of that extra goo, I decided to keep the implementation much more similar to the non-x86 implementation from .NETFX. Instead of intercepting the call and adding bookkeeping helper stubs, the marshaler just copies to a local in the IL stub, just like non-x86. When calling the native function, it just loads the local onto the IL stack and calls the native function as a normal function. There is a difference there, but I cannot think of a way that the difference is observable to the user. The non-x86 implementation is identical to the .NETFX implementation.
Diffstat (limited to 'tests')
-rw-r--r--tests/src/Interop/CMakeLists.txt1
-rw-r--r--tests/src/Interop/IJW/CopyConstructorMarshaler/CMakeLists.txt42
-rw-r--r--tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.cs66
-rw-r--r--tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.csproj44
-rw-r--r--tests/src/Interop/IJW/CopyConstructorMarshaler/IjwCopyConstructorMarshaler.cpp106
5 files changed, 259 insertions, 0 deletions
diff --git a/tests/src/Interop/CMakeLists.txt b/tests/src/Interop/CMakeLists.txt
index b4dec5750d..32e4b77e16 100644
--- a/tests/src/Interop/CMakeLists.txt
+++ b/tests/src/Interop/CMakeLists.txt
@@ -89,5 +89,6 @@ if(WIN32)
if(NOT CLR_CMAKE_PLATFORM_ARCH_ARM64)
add_subdirectory(IJW/ManagedCallingNative/IjwNativeDll)
add_subdirectory(IJW/NativeCallingManaged/IjwNativeCallingManagedDll)
+ add_subdirectory(IJW/CopyConstructorMarshaler)
endif()
endif(WIN32)
diff --git a/tests/src/Interop/IJW/CopyConstructorMarshaler/CMakeLists.txt b/tests/src/Interop/IJW/CopyConstructorMarshaler/CMakeLists.txt
new file mode 100644
index 0000000000..57726a4318
--- /dev/null
+++ b/tests/src/Interop/IJW/CopyConstructorMarshaler/CMakeLists.txt
@@ -0,0 +1,42 @@
+cmake_minimum_required (VERSION 2.6)
+project (CopyConstructorMarshaler)
+include_directories( ${INC_PLATFORM_DIR} )
+set(SOURCES IjwCopyConstructorMarshaler.cpp)
+
+if (WIN32)
+ # 4365 - signed/unsigned mismatch
+ add_compile_options(/wd4365)
+
+ # IJW
+ add_compile_options(/clr)
+
+ # IJW requires the CRT as a dll, not linked in
+ add_compile_options(/MD$<$<OR:$<CONFIG:Debug>,$<CONFIG:Checked>>:d>)
+
+ # CMake enables /RTC1 and /EHsc by default, but they're not compatible with /clr, so remove them
+ if(CMAKE_CXX_FLAGS_DEBUG MATCHES "/RTC1")
+ string(REPLACE "/RTC1" " " CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
+ endif()
+
+ if(CMAKE_CXX_FLAGS MATCHES "/EHsc")
+ string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+ endif()
+
+ # IJW isn't compatible with CFG
+ if(CMAKE_CXX_FLAGS MATCHES "/guard:cf")
+ string(REPLACE "/guard:cf" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+ endif()
+
+ # IJW isn't compatible with GR-
+ if(CMAKE_CXX_FLAGS MATCHES "/GR-")
+ string(REPLACE "/GR-" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+ endif()
+
+endif()
+
+# add the shared library
+add_library (IjwCopyConstructorMarshaler SHARED ${SOURCES})
+target_link_libraries(IjwCopyConstructorMarshaler ${LINK_LIBRARIES_ADDITIONAL})
+
+# add the install targets
+install (TARGETS IjwCopyConstructorMarshaler DESTINATION bin)
diff --git a/tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.cs b/tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.cs
new file mode 100644
index 0000000000..d589cd4fe8
--- /dev/null
+++ b/tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.cs
@@ -0,0 +1,66 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using TestLibrary;
+
+namespace CopyConstructorMarshaler
+{
+ class CopyConstructorMarshaler
+ {
+ static int Main(string[] args)
+ {
+ if(Environment.OSVersion.Platform != PlatformID.Win32NT || TestLibrary.Utilities.IsWindows7)
+ {
+ return 100;
+ }
+
+ try
+ {
+ // Load a fake mscoree.dll to avoid starting desktop
+ LoadLibraryEx(Path.Combine(Environment.CurrentDirectory, "mscoree.dll"), IntPtr.Zero, 0);
+
+ Assembly ijwNativeDll = Assembly.Load("IjwCopyConstructorMarshaler");
+ Type testType = ijwNativeDll.GetType("TestClass");
+ object testInstance = Activator.CreateInstance(testType);
+ MethodInfo testMethod = testType.GetMethod("PInvokeNumCopies");
+
+ // PInvoke will copy twice. Once from argument to parameter, and once from the managed to native parameter.
+ Assert.AreEqual(2, (int)testMethod.Invoke(testInstance, null));
+
+ testMethod = testType.GetMethod("ReversePInvokeNumCopies");
+
+ // Reverse PInvoke will copy 3 times. Two are from the same paths as the PInvoke,
+ // and the third is from the reverse P/Invoke call.
+ Assert.AreEqual(3, (int)testMethod.Invoke(testInstance, null));
+
+ testMethod = testType.GetMethod("PInvokeNumCopiesDerivedType");
+
+ // PInvoke will copy twice. Once from argument to parameter, and once from the managed to native parameter.
+ Assert.AreEqual(2, (int)testMethod.Invoke(testInstance, null));
+
+ testMethod = testType.GetMethod("ReversePInvokeNumCopiesDerivedType");
+
+ // Reverse PInvoke will copy 3 times. Two are from the same paths as the PInvoke,
+ // and the third is from the reverse P/Invoke call.
+ Assert.AreEqual(3, (int)testMethod.Invoke(testInstance, null));
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex);
+ return 101;
+ }
+ return 100;
+ }
+
+ [DllImport("kernel32.dll")]
+ static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, int dwFlags);
+
+ [DllImport("kernel32.dll")]
+ static extern IntPtr GetModuleHandle(string lpModuleName);
+ }
+}
diff --git a/tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.csproj b/tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.csproj
new file mode 100644
index 0000000000..34aad536a0
--- /dev/null
+++ b/tests/src/Interop/IJW/CopyConstructorMarshaler/CopyConstructorMarshaler.csproj
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Interop.settings.targets))\Interop.settings.targets" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <AssemblyName>CopyConstructorMarshaler</AssemblyName>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{49D1D482-E783-4CA9-B6BA-A9714BF81036}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+
+ <!-- IJW is Windows-only -->
+ <!-- Test unsupported outside of windows -->
+ <TestUnsupportedOutsideWindows>true</TestUnsupportedOutsideWindows>
+ <DisableProjectBuild Condition="'$(TargetsUnix)' == 'true'">true</DisableProjectBuild>
+
+ <!-- IJW is not supported on ARM64 -->
+ <DisableProjectBuild Condition="'$(Platform)' == 'arm64'">true</DisableProjectBuild>
+ </PropertyGroup>
+ <!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+ </PropertyGroup>
+ <PropertyGroup>
+ <CopyDebugCRTDllsToOutputDirectory>true</CopyDebugCRTDllsToOutputDirectory>
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="CopyConstructorMarshaler.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="CMakeLists.txt" />
+ <ProjectReference Include="../FakeMscoree/CMakeLists.txt" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
diff --git a/tests/src/Interop/IJW/CopyConstructorMarshaler/IjwCopyConstructorMarshaler.cpp b/tests/src/Interop/IJW/CopyConstructorMarshaler/IjwCopyConstructorMarshaler.cpp
new file mode 100644
index 0000000000..b82c33b349
--- /dev/null
+++ b/tests/src/Interop/IJW/CopyConstructorMarshaler/IjwCopyConstructorMarshaler.cpp
@@ -0,0 +1,106 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#pragma managed
+class A
+{
+ int copyCount;
+
+public:
+ A()
+ :copyCount(0)
+ {};
+
+ A(A& other)
+ :copyCount(other.copyCount + 1)
+ {}
+
+ int GetCopyCount()
+ {
+ return copyCount;
+ }
+};
+
+class B : public A
+{
+ int bCopyCount;
+
+public:
+ B()
+ :A(),
+ bCopyCount(0)
+ {};
+
+ B(B& other)
+ :A(other),
+ bCopyCount(other.bCopyCount + 1)
+ {}
+
+ int GetBCopyCount()
+ {
+ return bCopyCount;
+ }
+};
+
+int Managed_GetCopyCount(A a)
+{
+ return a.GetCopyCount();
+}
+
+int Managed_GetCopyCount(B b)
+{
+ return b.GetBCopyCount();
+}
+
+#pragma unmanaged
+
+int GetCopyCount(A a)
+{
+ return a.GetCopyCount();
+}
+
+int GetCopyCount_ViaManaged(A a)
+{
+ return Managed_GetCopyCount(a);
+}
+
+int GetCopyCount(B b)
+{
+ return b.GetBCopyCount();
+}
+
+int GetCopyCount_ViaManaged(B b)
+{
+ return Managed_GetCopyCount(b);
+}
+
+#pragma managed
+
+public ref class TestClass
+{
+public:
+ int PInvokeNumCopies()
+ {
+ A a;
+ return GetCopyCount(a);
+ }
+
+ int PInvokeNumCopiesDerivedType()
+ {
+ B b;
+ return GetCopyCount(b);
+ }
+
+ int ReversePInvokeNumCopies()
+ {
+ A a;
+ return GetCopyCount_ViaManaged(a);
+ }
+
+ int ReversePInvokeNumCopiesDerivedType()
+ {
+ B b;
+ return GetCopyCount_ViaManaged(b);
+ }
+};