diff --git a/src/coreclr/dlls/mscorrc/mscorrc.rc b/src/coreclr/dlls/mscorrc/mscorrc.rc index f11fe9431c8dd7..a774eb8214bb2b 100644 --- a/src/coreclr/dlls/mscorrc/mscorrc.rc +++ b/src/coreclr/dlls/mscorrc/mscorrc.rc @@ -593,6 +593,7 @@ BEGIN BFA_MISSING_DELEGATE_METHOD "Missing definition for required runtime implemented delegate method." BFA_MULT_TYPE_SAME_NAME "Duplicate type with name '%1' in assembly '%2'." BFA_INVALID_METHOD_TOKEN "Bad method token." + BFA_BAD_ASYNC_METHOD "Bad use of RuntimeAsync flag." BFA_ECALLS_MUST_BE_IN_SYS_MOD "ECall methods must be packaged into a system module." BFA_CANT_GET_CLASSLAYOUT "Could not get classlayout." BFA_CALLCONV_NOT_LOCAL_SIG "Signature is not IMAGE_CEE_CS_CALLCONV_LOCAL_SIG." diff --git a/src/coreclr/dlls/mscorrc/resource.h b/src/coreclr/dlls/mscorrc/resource.h index f3621378343c9a..822fd8b3b83397 100644 --- a/src/coreclr/dlls/mscorrc/resource.h +++ b/src/coreclr/dlls/mscorrc/resource.h @@ -366,6 +366,7 @@ #define BFA_MISSING_DELEGATE_METHOD 0x2030 #define BFA_MULT_TYPE_SAME_NAME 0x2031 #define BFA_INVALID_METHOD_TOKEN 0x2032 +#define BFA_BAD_ASYNC_METHOD 0x2033 #define BFA_ECALLS_MUST_BE_IN_SYS_MOD 0x2034 #define BFA_CANT_GET_CLASSLAYOUT 0x2035 #define BFA_CALLCONV_NOT_LOCAL_SIG 0x2036 diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index f1d765dc8b3844..e74f48d4eebcd1 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -4279,6 +4279,11 @@ private uint getJitFlags(ref CORJIT_FLAGS flags, uint sizeInBytes) ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramGenericMethod, this.MethodBeingCompiled); } + if (this.MethodBeingCompiled.IsAsync) + { + ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramAsync, this.MethodBeingCompiled); + } + #if READYTORUN // TODO: enable this check in full AOT if (Marshaller.IsMarshallingRequired(this.MethodBeingCompiled.Signature, ((MetadataType)this.MethodBeingCompiled.OwningType).Module, this.MethodBeingCompiled.GetUnmanagedCallersOnlyMethodCallingConventions())) // Only blittable arguments diff --git a/src/coreclr/tools/Common/TypeSystem/Common/ExceptionStringID.cs b/src/coreclr/tools/Common/TypeSystem/Common/ExceptionStringID.cs index 97363c783f8a3e..b2ea129eea4c21 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/ExceptionStringID.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/ExceptionStringID.cs @@ -39,6 +39,7 @@ public enum ExceptionStringID InvalidProgramGenericMethod, InvalidProgramNonBlittableTypes, InvalidProgramMultipleCallConv, + InvalidProgramAsync, // BadImageFormatException BadImageFormatGeneric, diff --git a/src/coreclr/tools/Common/TypeSystem/Common/Properties/Resources.resx b/src/coreclr/tools/Common/TypeSystem/Common/Properties/Resources.resx index 3fc29a898ac3dd..53f193ad0321f2 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/Properties/Resources.resx +++ b/src/coreclr/tools/Common/TypeSystem/Common/Properties/Resources.resx @@ -156,6 +156,9 @@ Failed to load assembly '{0}' + + UnmanagedCallersOnly attribute specified on async method '{0}' + Invalid IL or CLR metadata diff --git a/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs b/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs index 36b9c43b67e172..f4760fb0d257c1 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs @@ -23,6 +23,11 @@ private static MethodIL TryGetRuntimeImplementedMethodIL(MethodDesc method) Debug.Assert(method.IsRuntimeImplemented); + if (method.IsAsync) + { + ThrowHelper.ThrowBadImageFormatException(); + } + TypeDesc owningType = method.OwningType; if (owningType.IsDelegate) diff --git a/src/coreclr/tools/Common/TypeSystem/Interop/UnmanagedCallingConventions.cs b/src/coreclr/tools/Common/TypeSystem/Interop/UnmanagedCallingConventions.cs index 36cdc01805540b..87b1abf91cc1b6 100644 --- a/src/coreclr/tools/Common/TypeSystem/Interop/UnmanagedCallingConventions.cs +++ b/src/coreclr/tools/Common/TypeSystem/Interop/UnmanagedCallingConventions.cs @@ -103,7 +103,7 @@ public static UnmanagedCallingConventions GetPInvokeMethodCallingConventions(thi { Debug.Assert(method.IsPInvoke); - UnmanagedCallingConventions result; + UnmanagedCallingConventions result = 0; if (method is Internal.IL.Stubs.PInvokeTargetNativeMethod pinvokeTarget) method = pinvokeTarget.Target; @@ -116,9 +116,9 @@ public static UnmanagedCallingConventions GetPInvokeMethodCallingConventions(thi && (int)MethodSignatureFlags.UnmanagedCallingConventionThisCall == (int)UnmanagedCallingConventions.Thiscall); result = (UnmanagedCallingConventions)unmanagedCallConv; } - else + else if (method is EcmaMethod ecmaMethod) { - CustomAttributeValue? unmanagedCallConvAttribute = ((EcmaMethod)method).GetDecodedCustomAttribute("System.Runtime.InteropServices", "UnmanagedCallConvAttribute"); + CustomAttributeValue? unmanagedCallConvAttribute = ecmaMethod.GetDecodedCustomAttribute("System.Runtime.InteropServices", "UnmanagedCallConvAttribute"); if (unmanagedCallConvAttribute != null) { result = GetUnmanagedCallingConventionFromAttribute(unmanagedCallConvAttribute.Value, method.Context); @@ -127,10 +127,10 @@ public static UnmanagedCallingConventions GetPInvokeMethodCallingConventions(thi { result = GetPlatformDefaultUnmanagedCallingConvention(method.Context); } - } - if (method.HasCustomAttribute("System.Runtime.InteropServices", "SuppressGCTransitionAttribute")) - result |= UnmanagedCallingConventions.IsSuppressGcTransition; + if (method.HasCustomAttribute("System.Runtime.InteropServices", "SuppressGCTransitionAttribute")) + result |= UnmanagedCallingConventions.IsSuppressGcTransition; + } return result; } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/PInvokeILProvider.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/PInvokeILProvider.cs index 945284e43abc53..3b6c61264f1302 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/PInvokeILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/IL/Stubs/PInvokeILProvider.cs @@ -25,6 +25,10 @@ public PInvokeILProvider(PInvokeILEmitterConfiguration pInvokeILEmitterConfigura public override MethodIL GetMethodIL(MethodDesc method) { + if (method.IsAsync) + { + ThrowHelper.ThrowBadImageFormatException(); + } return PInvokeILEmitter.EmitIL(method, _pInvokeILEmitterConfiguration, _interopStateManager); } diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 75dd154611d003..2e4179eed9c3a0 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -2045,6 +2045,10 @@ void COMDelegate::ThrowIfInvalidUnmanagedCallersOnlyUsage(MethodDesc* pMD) if (pMD->HasClassOrMethodInstantiation()) EX_THROW(EEResourceException, (kInvalidProgramException, W("InvalidProgram_GenericMethod"))); + // No async methods + if (pMD->IsAsyncMethod()) + EX_THROW(EEResourceException, (kInvalidProgramException, W("InvalidProgram_AsyncMethod"))); + // Arguments - Scenarios involving UnmanagedCallersOnly are handled during the jit. bool unmanagedCallersOnlyRequiresMarshalling = false; if (PInvoke::MarshalingRequired(pMD, NULL, NULL, NULL, unmanagedCallersOnlyRequiresMarshalling)) diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index af8a66a0a992fc..be99ec36a97810 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -100,6 +100,7 @@ StubSigDesc::StubSigDesc(MethodDesc *pMD) GC_NOTRIGGER; SUPPORTS_DAC; PRECONDITION(pMD != NULL); + PRECONDITION(!pMD->IsAsyncMethod()); } CONTRACTL_END; @@ -108,9 +109,6 @@ StubSigDesc::StubSigDesc(MethodDesc *pMD) m_sig = pMD->GetSignature(); m_pModule = pMD->GetModule(); // Used for token resolution. - // TODO: (async) revisit and examine if this needs to be supported somehow - _ASSERTE(!pMD->IsAsyncMethod()); - m_tkMethodDef = pMD->GetMemberDef(); SigTypeContext::InitTypeContext(pMD, &m_typeContext); m_pMetadataModule = pMD->GetModule(); @@ -1131,7 +1129,6 @@ class ILStubState : public StubState DWORD dwToken = 0; if (pTargetMD) { - // TODO: (async) revisit and examine if this needs to be supported somehow _ASSERTE(!pTargetMD->IsAsyncVariantMethod()); dwToken = pTargetMD->GetMemberDef(); } @@ -2764,6 +2761,10 @@ void PInvokeStaticSigInfo::DllImportInit( PRECONDITION(CheckPointer(pMD)); + // P/Invoke methods should never be marked as async. + // This should be blocked in the class loader. + PRECONDITION(!pMD->IsAsyncMethod()); + // These preconditions to prevent multithreaded regression // where pMD->m_szLibName was passed in directly, cleared // by this API, then accessed on another thread before being reset here. @@ -2779,9 +2780,6 @@ void PInvokeStaticSigInfo::DllImportInit( IMDInternalImport *pInternalImport = pMD->GetMDImport(); CorPinvokeMap mappingFlags = pmMaxValue; mdModuleRef modref = mdModuleRefNil; - // TODO: (async) revisit and examine if this needs to be supported somehow - if (pMD->IsAsyncMethod()) - ThrowHR(COR_E_NOTSUPPORTED); if (FAILED(pInternalImport->GetPinvokeMap(pMD->GetMemberDef(), (DWORD*)&mappingFlags, ppEntryPointName, &modref))) { @@ -3053,6 +3051,7 @@ namespace STANDARD_VM_CHECK; PRECONDITION(pMD != NULL); PRECONDITION(pMD->IsPInvoke()); + PRECONDITION(!pMD->IsAsyncMethod()); PRECONDITION(callConv != NULL); } CONTRACTL_END; @@ -3060,9 +3059,7 @@ namespace CorInfoCallConvExtension callConvLocal; IMDInternalImport* pInternalImport = pMD->GetMDImport(); CorPinvokeMap mappingFlags = pmMaxValue; - // TODO: (async) revisit and examine if this needs to be supported somehow - if (pMD->IsAsyncMethod()) - ThrowHR(COR_E_NOTSUPPORTED); + HRESULT hr = pInternalImport->GetPinvokeMap(pMD->GetMemberDef(), (DWORD*)&mappingFlags, NULL /*pszImportName*/, NULL /*pmrImportDLL*/); if (FAILED(hr)) @@ -3249,6 +3246,12 @@ BOOL PInvoke::MarshalingRequired( { STANDARD_VM_CHECK; PRECONDITION(pMD != NULL || (!sigPointer.IsNull() && pModule != NULL)); + + // We should never see an async method here. + // Delegate Invoke methods should never be async. + // Async P/Invokes are not supported. + // Async UnmanagedCallersOnly methods are not supported. + PRECONDITION(pMD == NULL || !pMD->IsAsyncMethod()); } CONTRACTL_END; @@ -3324,10 +3327,6 @@ BOOL PInvoke::MarshalingRequired( mdMethodDef methodToken = mdMethodDefNil; if (pMD != NULL) { - // TODO: (async) revisit and examine if this needs to be supported somehow - if (pMD->IsAsyncMethod()) - ThrowHR(COR_E_NOTSUPPORTED); - methodToken = pMD->GetMemberDef(); } CollateParamTokens(pMDImport, methodToken, numArgs - 1, pParamTokenArray); @@ -6080,12 +6079,10 @@ static void GetILStubForCalli(VASigCookie* pVASigCookie, MethodDesc* pMD) if (pMD != NULL) { + _ASSERTE(pMD->IsPInvoke()); + _ASSERTE(!pMD->IsAsyncMethod()); PInvokeStaticSigInfo sigInfo(pMD); - // TODO: (async) revisit and examine if this needs to be supported somehow - if (pMD->IsAsyncMethod()) - ThrowHR(COR_E_NOTSUPPORTED); - md = pMD->GetMemberDef(); nlFlags = sigInfo.GetLinkFlags(); nlType = sigInfo.GetCharSet(); diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index a55ed3964c0f32..0bcc93a46f1f91 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -3183,6 +3183,11 @@ MethodTableBuilder::EnumerateClassMethods() CONSISTENCY_CHECK(hr == S_OK); type = mcPInvoke; } + + if (IsMiAsync(dwImplFlags)) + { + BuildMethodTableThrowException(BFA_BAD_ASYNC_METHOD); + } } else if (IsMiRuntime(dwImplFlags)) { @@ -3227,6 +3232,11 @@ MethodTableBuilder::EnumerateClassMethods() } delegateMethodsSeen |= newDelegateMethodSeen; + + if (IsMiAsync(dwImplFlags)) + { + BuildMethodTableThrowException(BFA_BAD_ASYNC_METHOD); + } } else if (hasGenericMethodArgs) { @@ -3254,6 +3264,12 @@ MethodTableBuilder::EnumerateClassMethods() // pointer-sized field pointing to COM interop data which are // allocated lazily when/if the MD actually gets used for interop. type = mcComInterop; + + // The interface method itself should never be marked as a runtime-async method. + if (IsMiAsync(dwImplFlags)) + { + BuildMethodTableThrowException(BFA_BAD_ASYNC_METHOD); + } } else #endif // !FEATURE_COMINTEROP @@ -3432,15 +3448,13 @@ MethodTableBuilder::EnumerateClassMethods() } MethodClassification asyncVariantType = type; -#ifdef FEATURE_COMINTEROP - if (type == mcComInterop) + if (type != mcIL && type != mcInstantiated) { - // For COM interop methods, - // we don't want to treat the async variant as a COM Interop method - // (as it isn't, it's a transient IL method). + // Don't treat the async variant of special method kinds as + // the special method kind. + // The async variant methods are always IL methods with a transient implementation. asyncVariantType = mcIL; } -#endif // FEATURE_COMINTEROP Signature newMemberSig(pNewMemberSignature, cAsyncThunkMemberSignature); pNewMethod = new (GetStackingAllocator()) bmtMDMethod( diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index bf34d7bec20f87..f5e642b7ee6ca8 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -3107,6 +3107,9 @@ Not supported in a non-reflected type. + + Async methods with UnmanagedCallersOnlyAttribute are invalid. + Non-static methods with UnmanagedCallersOnlyAttribute are invalid. diff --git a/src/tests/Interop/COM/RuntimeAsync/RuntimeAsync.cs b/src/tests/Interop/COM/RuntimeAsync/RuntimeAsync.cs index 529a4dec715ca3..74d239c2f16dd3 100644 --- a/src/tests/Interop/COM/RuntimeAsync/RuntimeAsync.cs +++ b/src/tests/Interop/COM/RuntimeAsync/RuntimeAsync.cs @@ -58,17 +58,36 @@ static async Task TestAsyncMethod(ITaskComServer_AsDispatchOnly obj) } } } + + [Fact] + public static void TaskReturningPInvokeWithComMarshalling() + { + Task originalTask = new(() => {}); + Task result = RunTask(originalTask); + + originalTask.Start(); + + result.GetAwaiter().GetResult(); + + static async Task RunTask(Task task) + { + await RuntimeAsyncNative.PassThroughTask(task); + } + } } public static class RuntimeAsyncNative { - [DllImport("RuntimeAsyncNative")] + [DllImport(nameof(RuntimeAsyncNative))] [return: MarshalAs(UnmanagedType.U1)] public static extern bool ValidateSlotLayoutForDefaultInterface([MarshalAs(UnmanagedType.Interface)] object comObject, int expectedIntValue, float expectedFloatValue); - [DllImport("RuntimeAsyncNative")] + [DllImport(nameof(RuntimeAsyncNative))] [return: MarshalAs(UnmanagedType.U1)] public static extern bool ValidateSlotLayoutForInterface([MarshalAs(UnmanagedType.Interface)] object comObject, float expectedFloatValue); + + [DllImport(nameof(RuntimeAsyncNative))] + public static extern Task PassThroughTask(Task task); } [ComVisible(true)] diff --git a/src/tests/Interop/COM/RuntimeAsync/RuntimeAsyncNative.cpp b/src/tests/Interop/COM/RuntimeAsync/RuntimeAsyncNative.cpp index d4e2b4a41cc942..da04e40a05ca5a 100644 --- a/src/tests/Interop/COM/RuntimeAsync/RuntimeAsyncNative.cpp +++ b/src/tests/Interop/COM/RuntimeAsync/RuntimeAsyncNative.cpp @@ -79,3 +79,9 @@ extern "C" DLL_EXPORT bool STDMETHODCALLTYPE ValidateSlotLayoutForInterface(IUnk return true; } + +extern "C" DLL_EXPORT IUnknown* STDMETHODCALLTYPE PassThroughTask(IUnknown* pUnk) +{ + pUnk->AddRef(); + return pUnk; +} diff --git a/src/tests/Interop/UnmanagedCallersOnly/InvalidCallbacks.il b/src/tests/Interop/UnmanagedCallersOnly/InvalidCallbacks.il index 7090382368e3b7..50edd29ba448bf 100644 --- a/src/tests/Interop/UnmanagedCallersOnly/InvalidCallbacks.il +++ b/src/tests/Interop/UnmanagedCallersOnly/InvalidCallbacks.il @@ -305,3 +305,41 @@ IL_0001: ret } } + +.class public auto ansi beforefieldinit InvalidCSharp.RuntimeAsyncUnmanagedCallersOnly + extends [System.Runtime]System.Object +{ + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + .maxstack 8 + ldarg.0 + call instance void [System.Runtime]System.Object::.ctor() + ret + } + + .method public hidebysig static + class [System.Runtime]System.Threading.Tasks.Task AsyncMethodReturningTask ( + ) cil managed async + { + .custom instance void [System.Runtime.InteropServices]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + .maxstack 8 + + IL_0001: ret + } + + .method public hidebysig static + int32 AsyncMethodWithBlittableReturnType ( + ) cil managed async + { + .custom instance void [System.Runtime.InteropServices]System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute::.ctor() = ( + 01 00 00 00 + ) + .maxstack 8 + + IL_0000: ldc.i4.0 + IL_0001: ret + } +} diff --git a/src/tests/Interop/UnmanagedCallersOnly/UnmanagedCallersOnlyTest.cs b/src/tests/Interop/UnmanagedCallersOnly/UnmanagedCallersOnlyTest.cs index 7d4d3c57d4d85a..702970b956b4a2 100644 --- a/src/tests/Interop/UnmanagedCallersOnly/UnmanagedCallersOnlyTest.cs +++ b/src/tests/Interop/UnmanagedCallersOnly/UnmanagedCallersOnlyTest.cs @@ -8,9 +8,11 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; +using System.Threading.Tasks; using Xunit; using InvalidCSharp; +[ActiveIssue("https://github.com/dotnet/runtime/issues/91388", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.PlatformDoesNotSupportNativeTestAssets))] public unsafe class Program { public static class UnmanagedCallersOnlyDll @@ -26,37 +28,6 @@ public static class UnmanagedCallersOnlyDll private delegate int IntNativeMethodInvoker(); private delegate void NativeMethodInvoker(); - [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/91388", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.PlatformDoesNotSupportNativeTestAssets))] - public static int TestEntryPoint() - { - try - { - NegativeTest_NonStaticMethod(); - NegativeTest_ViaDelegate(); - NegativeTest_NonBlittable(); - NegativeTest_InstantiatedGenericArguments(); - NegativeTest_FromInstantiatedGenericClass(); - TestUnmanagedCallersOnlyViaUnmanagedCalli(); - TestPInvokeMarkedWithUnmanagedCallersOnly(); - TestUnmanagedCallersOnlyWithGeneric(); - - // Exception handling interop is only supported on CoreCLR Windows. - if (TestLibrary.Utilities.IsWindows && !TestLibrary.Utilities.IsMonoRuntime && !TestLibrary.Utilities.IsCoreClrInterpreter) - { - TestUnmanagedCallersOnlyValid_ThrowException(); - TestUnmanagedCallersOnlyViaUnmanagedCalli_ThrowException(); - } - } - catch (Exception e) - { - Console.WriteLine($"Test Failure: {e}"); - return 101; - } - - return 100; - } - private static int DoubleImpl(int n) { return 2 * n; @@ -70,6 +41,7 @@ public static int CallbackThrows(int val) throw new Exception() { HResult = CallbackThrowsErrorCode }; } + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsExceptionInteropSupported))] public static void TestUnmanagedCallersOnlyValid_ThrowException() { Console.WriteLine($"Running {nameof(TestUnmanagedCallersOnlyValid_ThrowException)}..."); @@ -79,6 +51,7 @@ public static void TestUnmanagedCallersOnlyValid_ThrowException() Assert.Equal(-1, UnmanagedCallersOnlyDll.CallManagedProcCatchException((IntPtr)(delegate* unmanaged)&CallbackThrows, n)); } + [Fact] public static void NegativeTest_ViaDelegate() { Console.WriteLine($"Running {nameof(NegativeTest_ViaDelegate)}..."); @@ -94,6 +67,7 @@ void CallAsDelegate() } } + [Fact] public static void NegativeTest_NonStaticMethod() { Console.WriteLine($"Running {nameof(NegativeTest_NonStaticMethod)}..."); @@ -109,6 +83,7 @@ public static int CallbackMethodNonBlittable(bool x1) return -1; } + [Fact] public static void NegativeTest_NonBlittable() { Console.WriteLine($"Running {nameof(NegativeTest_NonBlittable)}..."); @@ -121,6 +96,7 @@ public static void NegativeTest_NonBlittable() Assert.Throws(() => { UnmanagedCallersOnlyDll.CallManagedProc(UnmanagedCallersOnlyWithByRefs.GetWithByRefOutFunctionPointer(), n); }); } + [Fact] public static void NegativeTest_InstantiatedGenericArguments() { Console.WriteLine($"Running {nameof(NegativeTest_InstantiatedGenericArguments)}..."); @@ -130,6 +106,7 @@ public static void NegativeTest_InstantiatedGenericArguments() Assert.Throws(() => { UnmanagedCallersOnlyDll.CallManagedProc((IntPtr)(delegate* unmanaged)&Callbacks.CallbackMethodGeneric, n); }); } + [Fact] public static void NegativeTest_FromInstantiatedGenericClass() { Console.WriteLine($"Running {nameof(NegativeTest_FromInstantiatedGenericClass)}..."); @@ -145,6 +122,7 @@ public static int CallbackViaUnmanagedCalli(int val) return DoubleImpl(val); } + [Fact] public static void TestUnmanagedCallersOnlyViaUnmanagedCalli() { Console.WriteLine($"Running {nameof(TestUnmanagedCallersOnlyViaUnmanagedCalli)}..."); @@ -161,6 +139,7 @@ public static int CallbackViaUnmanagedCalliThrows(int val) throw new Exception() { HResult = CallbackThrowsErrorCode }; } + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsExceptionInteropSupported))] public static void TestUnmanagedCallersOnlyViaUnmanagedCalli_ThrowException() { Console.WriteLine($"Running {nameof(TestUnmanagedCallersOnlyViaUnmanagedCalli_ThrowException)}..."); @@ -179,6 +158,7 @@ public static void TestUnmanagedCallersOnlyViaUnmanagedCalli_ThrowException() } } + [Fact] public static void TestPInvokeMarkedWithUnmanagedCallersOnly() { Console.WriteLine($"Running {nameof(TestPInvokeMarkedWithUnmanagedCallersOnly)}..."); @@ -195,6 +175,7 @@ public static void TestPInvokeMarkedWithUnmanagedCallersOnly() Assert.Throws(() => ((delegate* unmanaged)&CallingUnmanagedCallersOnlyDirectly.PInvokeMarkedWithUnmanagedCallersOnly)(n)); } + [Fact] public static void TestUnmanagedCallersOnlyWithGeneric() { Assert.Equal(0, ((delegate* unmanaged, int>)&BlittableGenericStruct)(new Blittable())); @@ -209,6 +190,20 @@ public static void TestUnmanagedCallersOnlyWithGeneric() => ((delegate* unmanaged)(void*)(delegate* unmanaged, int>)&InvalidGenericUnmanagedCallersOnlyParameters.GenericStructWithObjectField)((nint)1)); } + [Fact] + [SkipOnMono("Mono doesn't support runtime async and doesn't check the async bit.")] + public static void TestUnmanagedCallersOnlyWithRuntimeAsync() + { + AssertExtensions.ThrowsAny(() => + { + ((delegate* unmanaged)&RuntimeAsyncUnmanagedCallersOnly.AsyncMethodReturningTask)(); + }); + AssertExtensions.ThrowsAny(() => + { + ((delegate* unmanaged)&RuntimeAsyncUnmanagedCallersOnly.AsyncMethodWithBlittableReturnType)(); + }); + } + internal struct Blittable where T : unmanaged { T Value;