From 40f76b1e5c0d661ef02f1e6f3e5e37678499aae4 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Wed, 1 Jun 2022 13:39:00 -0700 Subject: [PATCH] Add exception interop test for validating native exception interop on Windows + CoreCLR. --- .../exceptioninterop/CMakeLists.txt | 4 + .../exceptioninterop/ExceptionInterop.cs | 125 ++++++++++++++++++ .../exceptioninterop/ExceptionInterop.csproj | 10 ++ .../ExceptionInteropNative.cpp | 19 +++ .../ExceptionInterop_ro.csproj | 14 ++ 5 files changed, 172 insertions(+) create mode 100644 src/tests/baseservices/exceptions/exceptioninterop/CMakeLists.txt create mode 100644 src/tests/baseservices/exceptions/exceptioninterop/ExceptionInterop.cs create mode 100644 src/tests/baseservices/exceptions/exceptioninterop/ExceptionInterop.csproj create mode 100644 src/tests/baseservices/exceptions/exceptioninterop/ExceptionInteropNative.cpp create mode 100644 src/tests/baseservices/exceptions/exceptioninterop/ExceptionInterop_ro.csproj diff --git a/src/tests/baseservices/exceptions/exceptioninterop/CMakeLists.txt b/src/tests/baseservices/exceptions/exceptioninterop/CMakeLists.txt new file mode 100644 index 00000000000000..a477a57c1cdbdc --- /dev/null +++ b/src/tests/baseservices/exceptions/exceptioninterop/CMakeLists.txt @@ -0,0 +1,4 @@ + +include_directories(${INC_PLATFORM_DIR}) +add_library(ExceptionInteropNative SHARED ExceptionInteropNative.cpp) +target_link_libraries(ExceptionInteropNative PRIVATE platformdefines) \ No newline at end of file diff --git a/src/tests/baseservices/exceptions/exceptioninterop/ExceptionInterop.cs b/src/tests/baseservices/exceptions/exceptioninterop/ExceptionInterop.cs new file mode 100644 index 00000000000000..f9f18b91369de9 --- /dev/null +++ b/src/tests/baseservices/exceptions/exceptioninterop/ExceptionInterop.cs @@ -0,0 +1,125 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Xunit; +using static ExceptionInteropNative; + +internal unsafe static class ExceptionInteropNative +{ + [DllImport(nameof(ExceptionInteropNative))] + public static extern void ThrowException(); + + [DllImport(nameof(ExceptionInteropNative))] + public static extern void NativeFunction(); + + [DllImport(nameof(ExceptionInteropNative))] + public static extern void CallCallback(delegate* unmanaged cb); +} + +public unsafe static class ExceptionInterop +{ + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + [SkipOnMono("Exception interop not supported on Mono.")] + public static void ThrowNativeExceptionAndCatchInFrame() + { + bool caughtException = false; + try + { + ThrowException(); + } + catch + { + caughtException = true; + // Try calling another P/Invoke in the catch block to make sure we have everything set up + // to recover from the exceptional control flow. + NativeFunction(); + } + Assert.True(caughtException); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + [SkipOnMono("Exception interop not supported on Mono.")] + public static void ThrowManagedExceptionThroughNativeAndCatchInFrame() + { + bool caughtException = false; + try + { + CallCallback(&ThrowManagedException); + } + catch + { + caughtException = true; + // Try calling another P/Invoke in the catch block to make sure we have everything set up + // to recover from the exceptional control flow. + NativeFunction(); + } + Assert.True(caughtException); + + [UnmanagedCallersOnly] + static void ThrowManagedException() + { + throw new Exception(); + } + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + [SkipOnMono("Exception interop not supported on Mono.")] + public static void ThrowNativeExceptionAndCatchInFrameWithFilter() + { + bool caughtException = false; + try + { + ThrowException(); + } + catch (Exception) when (Filter()) + { + caughtException = true; + // Try calling another P/Invoke in the catch block to make sure we have everything set up + // to recover from the exceptional control flow. + NativeFunction(); + } + Assert.True(caughtException); + + // Aggresively inline to make sure the call to NativeFunction is in the filter clause + [MethodImpl(MethodImplOptions.AggressiveInlining)] + bool Filter() + { + NativeFunction(); + return true; + } + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + [SkipOnMono("Exception interop not supported on Mono.")] + public static void ThrowNativeExceptionAndCatchInFrameWithFinally() + { + bool caughtException = false; + try + { + try + { + ThrowException(); + } + finally + { + // Try calling another P/Invoke in the finally block before the catch + // to make sure we have everything set up + // to recover from the exceptional control flow. + NativeFunction(); + } + } + catch + { + caughtException = true; + } + + Assert.True(caughtException); + } +} \ No newline at end of file diff --git a/src/tests/baseservices/exceptions/exceptioninterop/ExceptionInterop.csproj b/src/tests/baseservices/exceptions/exceptioninterop/ExceptionInterop.csproj new file mode 100644 index 00000000000000..d553892a3b2507 --- /dev/null +++ b/src/tests/baseservices/exceptions/exceptioninterop/ExceptionInterop.csproj @@ -0,0 +1,10 @@ + + + true + + + + + + + \ No newline at end of file diff --git a/src/tests/baseservices/exceptions/exceptioninterop/ExceptionInteropNative.cpp b/src/tests/baseservices/exceptions/exceptioninterop/ExceptionInteropNative.cpp new file mode 100644 index 00000000000000..2d141792dd7b05 --- /dev/null +++ b/src/tests/baseservices/exceptions/exceptioninterop/ExceptionInteropNative.cpp @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#include +#include + +extern "C" DLL_EXPORT void STDMETHODCALLTYPE ThrowException() +{ + throw std::exception{}; +} + +extern "C" DLL_EXPORT void STDMETHODCALLTYPE NativeFunction() +{ +} + +extern "C" DLL_EXPORT void STDMETHODCALLTYPE CallCallback(void (*cb)()) +{ + cb(); +} \ No newline at end of file diff --git a/src/tests/baseservices/exceptions/exceptioninterop/ExceptionInterop_ro.csproj b/src/tests/baseservices/exceptions/exceptioninterop/ExceptionInterop_ro.csproj new file mode 100644 index 00000000000000..99c7f983705cda --- /dev/null +++ b/src/tests/baseservices/exceptions/exceptioninterop/ExceptionInterop_ro.csproj @@ -0,0 +1,14 @@ + + + true + + + None + True + + + + + + + \ No newline at end of file