From b6f5473de67810c74ca6fc96619a21289dbd7d26 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 27 Feb 2024 12:27:37 -0800 Subject: [PATCH] Revert "Support generic arguments for calli in CoreCLR (#97079)" This reverts commit 15ff723dfe6b0fc90797c8b3117b29e51bcddab0. --- src/coreclr/vm/ceeload.cpp | 112 +++--------------- src/coreclr/vm/ceeload.h | 7 +- src/coreclr/vm/comdelegate.cpp | 2 +- src/coreclr/vm/dllimport.cpp | 33 ++---- src/coreclr/vm/dllimport.h | 18 +-- src/coreclr/vm/ilstubcache.cpp | 21 ++-- src/coreclr/vm/ilstubcache.h | 2 - src/coreclr/vm/jitinterface.cpp | 14 +-- src/tests/Interop/Interop.csproj | 2 - .../FunctionPointer/FunctionPointer.cs | 6 +- .../FunctionPointer/GenericFunctionPointer.cs | 107 ----------------- 11 files changed, 46 insertions(+), 278 deletions(-) delete mode 100644 src/tests/Interop/MarshalAPI/FunctionPointer/GenericFunctionPointer.cs diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index e05302f536706..0796e59a15c29 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -4680,7 +4680,7 @@ PTR_VOID ReflectionModule::GetRvaField(RVA field) // virtual //========================================================================== // Enregisters a VASig. //========================================================================== -VASigCookie *Module::GetVASigCookie(Signature vaSignature, const SigTypeContext* typeContext) +VASigCookie *Module::GetVASigCookie(Signature vaSignature) { CONTRACT(VASigCookie*) { @@ -4693,24 +4693,6 @@ VASigCookie *Module::GetVASigCookie(Signature vaSignature, const SigTypeContext* } CONTRACT_END; - Module* pLoaderModule = ClassLoader::ComputeLoaderModuleWorker(this, mdTokenNil, typeContext->m_classInst, typeContext->m_methodInst); - VASigCookie *pCookie = GetVASigCookieWorker(this, pLoaderModule, vaSignature, typeContext); - - RETURN pCookie; -} - -VASigCookie *Module::GetVASigCookieWorker(Module* pDefiningModule, Module* pLoaderModule, Signature vaSignature, const SigTypeContext* typeContext) -{ - CONTRACT(VASigCookie*) - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - POSTCONDITION(CheckPointer(RETVAL)); - INJECT_FAULT(COMPlusThrowOM()); - } - CONTRACT_END; - VASigCookieBlock *pBlock; VASigCookie *pCookie; @@ -4718,70 +4700,39 @@ VASigCookie *Module::GetVASigCookieWorker(Module* pDefiningModule, Module* pLoad // First, see if we already enregistered this sig. // Note that we're outside the lock here, so be a bit careful with our logic - for (pBlock = pLoaderModule->m_pVASigCookieBlock; pBlock != NULL; pBlock = pBlock->m_Next) + for (pBlock = m_pVASigCookieBlock; pBlock != NULL; pBlock = pBlock->m_Next) { for (UINT i = 0; i < pBlock->m_numcookies; i++) { if (pBlock->m_cookies[i].signature.GetRawSig() == vaSignature.GetRawSig()) { - _ASSERTE(pBlock->m_cookies[i].classInst.GetNumArgs() == typeContext->m_classInst.GetNumArgs()); - _ASSERTE(pBlock->m_cookies[i].methodInst.GetNumArgs() == typeContext->m_methodInst.GetNumArgs()); - - bool instMatch = true; - - for (DWORD j = 0; j < pBlock->m_cookies[i].classInst.GetNumArgs(); j++) - { - if (pBlock->m_cookies[i].classInst[j] != typeContext->m_classInst[j]) - { - instMatch = false; - break; - } - } - - if (instMatch) - { - for (DWORD j = 0; j < pBlock->m_cookies[i].methodInst.GetNumArgs(); j++) - { - if (pBlock->m_cookies[i].methodInst[j] != typeContext->m_methodInst[j]) - { - instMatch = false; - break; - } - } - } - - if (instMatch) - { - pCookie = &(pBlock->m_cookies[i]); - break; - } + pCookie = &(pBlock->m_cookies[i]); + break; } } } - + if (!pCookie) { // If not, time to make a new one. // Compute the size of args first, outside of the lock. - MetaSig metasig(vaSignature, pDefiningModule, typeContext); + // @TODO GENERICS: We may be calling a varargs method from a + // generic type/method. Using an empty context will make such a + // case cause an unexpected exception. To make this work, + // we need to create a specialized signature for every instantiation + SigTypeContext typeContext; + + MetaSig metasig(vaSignature, this, &typeContext); ArgIterator argit(&metasig); // Upper estimate of the vararg size DWORD sizeOfArgs = argit.SizeOfArgStack(); - // Prepare instantiation - LoaderAllocator *pLoaderAllocator = pLoaderModule->GetLoaderAllocator(); - - DWORD classInstCount = typeContext->m_classInst.GetNumArgs(); - DWORD methodInstCount = typeContext->m_methodInst.GetNumArgs(); - pLoaderAllocator->EnsureInstantiation(pDefiningModule, typeContext->m_classInst); - pLoaderAllocator->EnsureInstantiation(pDefiningModule, typeContext->m_methodInst); - // enable gc before taking lock { - CrstHolder ch(&pLoaderModule->m_Crst); + CrstHolder ch(&m_Crst); // Note that we were possibly racing to create the cookie, and another thread // may have already created it. We could put another check @@ -4789,57 +4740,32 @@ VASigCookie *Module::GetVASigCookieWorker(Module* pDefiningModule, Module* pLoad // occasional duplicate cookie instead. // Is the first block in the list full? - if (pLoaderModule->m_pVASigCookieBlock && pLoaderModule->m_pVASigCookieBlock->m_numcookies + if (m_pVASigCookieBlock && m_pVASigCookieBlock->m_numcookies < VASigCookieBlock::kVASigCookieBlockSize) { // Nope, reserve a new slot in the existing block. - pCookie = &(pLoaderModule->m_pVASigCookieBlock->m_cookies[pLoaderModule->m_pVASigCookieBlock->m_numcookies]); + pCookie = &(m_pVASigCookieBlock->m_cookies[m_pVASigCookieBlock->m_numcookies]); } else { // Yes, create a new block. VASigCookieBlock *pNewBlock = new VASigCookieBlock(); - pNewBlock->m_Next = pLoaderModule->m_pVASigCookieBlock; + pNewBlock->m_Next = m_pVASigCookieBlock; pNewBlock->m_numcookies = 0; - pLoaderModule->m_pVASigCookieBlock = pNewBlock; + m_pVASigCookieBlock = pNewBlock; pCookie = &(pNewBlock->m_cookies[0]); } // Now, fill in the new cookie (assuming we had enough memory to create one.) - pCookie->pModule = pDefiningModule; + pCookie->pModule = this; pCookie->pNDirectILStub = NULL; pCookie->sizeOfArgs = sizeOfArgs; pCookie->signature = vaSignature; - pCookie->pLoaderModule = pLoaderModule; - - AllocMemTracker amt; - - if (classInstCount != 0) - { - TypeHandle* pClassInst = (TypeHandle*)(void*)amt.Track(pLoaderAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(classInstCount) * S_SIZE_T(sizeof(TypeHandle)))); - for (DWORD i = 0; i < classInstCount; i++) - { - pClassInst[i] = typeContext->m_classInst[i]; - } - pCookie->classInst = Instantiation(pClassInst, classInstCount); - } - - if (methodInstCount != 0) - { - TypeHandle* pMethodInst = (TypeHandle*)(void*)amt.Track(pLoaderAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(methodInstCount) * S_SIZE_T(sizeof(TypeHandle)))); - for (DWORD i = 0; i < methodInstCount; i++) - { - pMethodInst[i] = typeContext->m_methodInst[i]; - } - pCookie->methodInst = Instantiation(pMethodInst, methodInstCount); - } - - amt.SuppressRelease(); // Finally, now that it's safe for asynchronous readers to see it, // update the count. - pLoaderModule->m_pVASigCookieBlock->m_numcookies++; + m_pVASigCookieBlock->m_numcookies++; } } diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index 3ad7b37a76a3d..18335c5a5f01a 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -338,10 +338,7 @@ struct VASigCookie unsigned sizeOfArgs; // size of argument list Volatile pNDirectILStub; // will be use if target is NDirect (tag == 0) PTR_Module pModule; - PTR_Module pLoaderModule; Signature signature; - Instantiation classInst; - Instantiation methodInst; }; // @@ -1363,9 +1360,7 @@ class Module : public ModuleBase void NotifyEtwLoadFinished(HRESULT hr); // Enregisters a VASig. - VASigCookie *GetVASigCookie(Signature vaSignature, const SigTypeContext* typeContext); -private: - static VASigCookie *GetVASigCookieWorker(Module* pDefiningModule, Module* pLoaderModule, Signature vaSignature, const SigTypeContext* typeContext); + VASigCookie *GetVASigCookie(Signature vaSignature); public: #ifndef DACCESS_COMPILE diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index ef4021039a66b..7e7a4de4b02af 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -2019,7 +2019,7 @@ void COMDelegate::ThrowIfInvalidUnmanagedCallersOnlyUsage(MethodDesc* pMD) // Arguments - Scenarios involving UnmanagedCallersOnly are handled during the jit. bool unmanagedCallersOnlyRequiresMarshalling = false; - if (NDirect::MarshalingRequired(pMD, NULL, NULL, NULL, unmanagedCallersOnlyRequiresMarshalling)) + if (NDirect::MarshalingRequired(pMD, NULL, NULL, unmanagedCallersOnlyRequiresMarshalling)) EX_THROW(EEResourceException, (kInvalidProgramException, W("InvalidProgram_NonBlittableTypes"))); } diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index bc106bf0f43d8..d395b8e32cf5c 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -113,7 +113,7 @@ StubSigDesc::StubSigDesc(MethodDesc *pMD) INDEBUG(InitDebugNames()); } -StubSigDesc::StubSigDesc(MethodDesc* pMD, const Signature& sig, Module* pModule, Module* pLoaderModule) +StubSigDesc::StubSigDesc(MethodDesc* pMD, const Signature& sig, Module* pModule) { CONTRACTL { @@ -135,13 +135,13 @@ StubSigDesc::StubSigDesc(MethodDesc* pMD, const Signature& sig, Module* pModule, m_tkMethodDef = pMD->GetMemberDef(); SigTypeContext::InitTypeContext(pMD, &m_typeContext); m_pMetadataModule = pMD->GetModule(); - m_pLoaderModule = pLoaderModule == NULL ? pMD->GetLoaderModule() : pLoaderModule; // Used for ILStubCache selection and MethodTable creation. + m_pLoaderModule = pMD->GetLoaderModule(); // Used for ILStubCache selection and MethodTable creation. } else { m_tkMethodDef = mdMethodDefNil; m_pMetadataModule = m_pModule; - m_pLoaderModule = pLoaderModule == NULL ? m_pModule : pLoaderModule; + m_pLoaderModule = m_pModule; } INDEBUG(InitDebugNames()); @@ -3180,7 +3180,6 @@ BOOL NDirect::MarshalingRequired( _In_opt_ MethodDesc* pMD, _In_opt_ PCCOR_SIGNATURE pSig, _In_opt_ Module* pModule, - _In_opt_ SigTypeContext* pTypeContext, _In_ bool unmanagedCallersOnlyRequiresMarshalling) { CONTRACTL @@ -3261,6 +3260,8 @@ BOOL NDirect::MarshalingRequired( mdParamDef *pParamTokenArray = (mdParamDef *)_alloca(numArgs * sizeof(mdParamDef)); IMDInternalImport *pMDImport = pModule->GetMDImport(); + SigTypeContext emptyTypeContext; + mdMethodDef methodToken = mdMethodDefNil; if (pMD != NULL) { @@ -3320,7 +3321,7 @@ BOOL NDirect::MarshalingRequired( case ELEMENT_TYPE_VALUETYPE: case ELEMENT_TYPE_GENERICINST: { - TypeHandle hndArgType = arg.GetTypeHandleThrowing(pModule, pTypeContext); + TypeHandle hndArgType = arg.GetTypeHandleThrowing(pModule, &emptyTypeContext); bool isValidGeneric = IsValidForGenericMarshalling(hndArgType.GetMethodTable(), false, runtimeMarshallingEnabled); if(!hndArgType.IsValueType() || !isValidGeneric) return true; @@ -4191,10 +4192,8 @@ namespace pHashParams, pParams->m_dwStubFlags, pParams->m_pModule, - pParams->m_pLoaderModule, pParams->m_sig.GetRawSig(), pParams->m_sig.GetRawSigLen(), - pParams->m_pTypeContext, pamTracker, bILStubCreator, pLastMD); @@ -5042,21 +5041,6 @@ namespace } else { - if (!pSigDesc->m_typeContext.IsEmpty()) - { - // For generic calli, we only support blittable types - if (SF_IsCALLIStub(dwStubFlags) - && NDirect::MarshalingRequired(NULL, pStubMD->GetSig(), pSigDesc->m_pModule, &pSigDesc->m_typeContext)) - { - COMPlusThrow(kMarshalDirectiveException, IDS_EE_BADMARSHAL_GENERICS_RESTRICTION); - } - // We don't want to support generic varargs, so block it - else if (SF_IsVarArgStub(dwStubFlags)) - { - COMPlusThrow(kNotSupportedException, BFA_GENCODE_NOT_BE_VARARG); - } - } - CreateNDirectStubWorker(pss, pSigDesc, nlType, @@ -6043,7 +6027,7 @@ PCODE GetILStubForCalli(VASigCookie *pVASigCookie, MethodDesc *pMD) } } - LoaderHeap *pHeap = pVASigCookie->pLoaderModule->GetLoaderAllocator()->GetHighFrequencyHeap(); + LoaderHeap *pHeap = pVASigCookie->pModule->GetLoaderAllocator()->GetHighFrequencyHeap(); PCOR_SIGNATURE new_sig = (PCOR_SIGNATURE)(void *)pHeap->AllocMem(S_SIZE_T(signature.GetRawSigLen())); CopyMemory(new_sig, signature.GetRawSig(), signature.GetRawSigLen()); @@ -6081,8 +6065,7 @@ PCODE GetILStubForCalli(VASigCookie *pVASigCookie, MethodDesc *pMD) nlType = nltAnsi; } - StubSigDesc sigDesc(pMD, signature, pVASigCookie->pModule, pVASigCookie->pLoaderModule); - sigDesc.InitTypeContext(pVASigCookie->classInst, pVASigCookie->methodInst); + StubSigDesc sigDesc(pMD, signature, pVASigCookie->pModule); MethodDesc* pStubMD = NDirect::CreateCLRToNativeILStub(&sigDesc, nlType, diff --git a/src/coreclr/vm/dllimport.h b/src/coreclr/vm/dllimport.h index 111c7436c9473..256b950799336 100644 --- a/src/coreclr/vm/dllimport.h +++ b/src/coreclr/vm/dllimport.h @@ -16,9 +16,9 @@ struct StubSigDesc { public: StubSigDesc(MethodDesc* pMD); - StubSigDesc(MethodDesc* pMD, const Signature& sig, Module* pModule, Module* pLoaderModule = NULL); - StubSigDesc(MethodTable* pMT, const Signature& sig, Module* pModule); - StubSigDesc(const Signature& sig, Module* pModule); + StubSigDesc(MethodDesc* pMD, const Signature& sig, Module* m_pModule); + StubSigDesc(MethodTable* pMT, const Signature& sig, Module* m_pModule); + StubSigDesc(const Signature& sig, Module* m_pModule); MethodDesc *m_pMD; MethodTable *m_pMT; @@ -56,17 +56,6 @@ struct StubSigDesc } } #endif // _DEBUG - -#ifndef DACCESS_COMPILE - void InitTypeContext(Instantiation classInst, Instantiation methodInst) - { - LIMITED_METHOD_CONTRACT; - - _ASSERTE(m_typeContext.IsEmpty()); - - m_typeContext = SigTypeContext(classInst, methodInst); - } -#endif }; //======================================================================= @@ -103,7 +92,6 @@ class NDirect _In_opt_ MethodDesc* pMD, _In_opt_ PCCOR_SIGNATURE pSig = NULL, _In_opt_ Module* pModule = NULL, - _In_opt_ SigTypeContext* pTypeContext = NULL, _In_ bool unmanagedCallersOnlyRequiresMarshalling = true); static void PopulateNDirectMethodDesc(_Inout_ NDirectMethodDesc* pNMD); diff --git a/src/coreclr/vm/ilstubcache.cpp b/src/coreclr/vm/ilstubcache.cpp index 1d8d14456a1f0..d0f55495c8290 100644 --- a/src/coreclr/vm/ilstubcache.cpp +++ b/src/coreclr/vm/ilstubcache.cpp @@ -500,10 +500,8 @@ MethodDesc* ILStubCache::GetStubMethodDesc( ILStubHashBlob* pHashBlob, DWORD dwStubFlags, Module* pSigModule, - Module* pSigLoaderModule, PCCOR_SIGNATURE pSig, DWORD cbSig, - SigTypeContext* pTypeContext, AllocMemTracker* pamTracker, bool& bILStubCreator, MethodDesc *pLastMD) @@ -540,23 +538,22 @@ MethodDesc* ILStubCache::GetStubMethodDesc( // Couldn't find it, let's make a new one. // - if (pSigLoaderModule == NULL) + Module *pContainingModule = pSigModule; + if (pTargetMD != NULL) { - pSigLoaderModule = (pTargetMD != NULL) ? pTargetMD->GetLoaderModule() : pSigModule; + // loader module may be different from signature module for generic targets + pContainingModule = pTargetMD->GetLoaderModule(); } + MethodTable *pStubMT = GetOrCreateStubMethodTable(pContainingModule); + SigTypeContext typeContext; - if (pTypeContext == NULL) + if (pTargetMD != NULL) { - if (pTargetMD != NULL) - { - SigTypeContext::InitTypeContext(pTargetMD, &typeContext); - } - pTypeContext = &typeContext; + SigTypeContext::InitTypeContext(pTargetMD, &typeContext); } - MethodTable *pStubMT = GetOrCreateStubMethodTable(pSigLoaderModule); - pMD = ILStubCache::CreateNewMethodDesc(m_pAllocator->GetHighFrequencyHeap(), pStubMT, dwStubFlags, pSigModule, pSig, cbSig, pTypeContext, pamTracker); + pMD = ILStubCache::CreateNewMethodDesc(m_pAllocator->GetHighFrequencyHeap(), pStubMT, dwStubFlags, pSigModule, pSig, cbSig, &typeContext, pamTracker); if (SF_IsSharedStub(dwStubFlags)) { diff --git a/src/coreclr/vm/ilstubcache.h b/src/coreclr/vm/ilstubcache.h index c53fd7a1878c2..6324bad28eebf 100644 --- a/src/coreclr/vm/ilstubcache.h +++ b/src/coreclr/vm/ilstubcache.h @@ -53,10 +53,8 @@ class ILStubCache final ILStubHashBlob* pHashBlob, DWORD dwStubFlags, // bitmask of NDirectStubFlags Module* pSigModule, - Module* pSigLoaderModule, PCCOR_SIGNATURE pSig, DWORD cbSig, - SigTypeContext* pTypeContext, AllocMemTracker* pamTracker, bool& bILStubCreator, MethodDesc* pLastMD); diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index aa756361340b6..a2146482a2fc0 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -6379,11 +6379,7 @@ CORINFO_VARARGS_HANDLE CEEInfo::getVarArgsHandle(CORINFO_SIG_INFO *sig, Module* module = GetModule(sig->scope); - Instantiation classInst = Instantiation((TypeHandle*) sig->sigInst.classInst, sig->sigInst.classInstCount); - Instantiation methodInst = Instantiation((TypeHandle*) sig->sigInst.methInst, sig->sigInst.methInstCount); - SigTypeContext typeContext = SigTypeContext(classInst, methodInst); - - result = CORINFO_VARARGS_HANDLE(module->GetVASigCookie(Signature(sig->pSig, sig->cbSig), &typeContext)); + result = CORINFO_VARARGS_HANDLE(module->GetVASigCookie(Signature(sig->pSig, sig->cbSig))); EE_TO_JIT_TRANSITION(); @@ -9874,13 +9870,10 @@ bool CEEInfo::pInvokeMarshalingRequired(CORINFO_METHOD_HANDLE method, CORINFO_SI if (method == NULL) { // check the call site signature - SigTypeContext typeContext; - GetTypeContext(&callSiteSig->sigInst, &typeContext); result = NDirect::MarshalingRequired( NULL, callSiteSig->pSig, - GetModule(callSiteSig->scope), - &typeContext); + GetModule(callSiteSig->scope)); } else { @@ -13534,8 +13527,7 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, } { VarArgs: - SigTypeContext typeContext = SigTypeContext(); - result = (size_t) CORINFO_VARARGS_HANDLE(currentModule->GetVASigCookie(Signature(pSig, cSig), &typeContext)); + result = (size_t) CORINFO_VARARGS_HANDLE(currentModule->GetVASigCookie(Signature(pSig, cSig))); } break; diff --git a/src/tests/Interop/Interop.csproj b/src/tests/Interop/Interop.csproj index 0e25aba816182..6fc22f7bf4f27 100644 --- a/src/tests/Interop/Interop.csproj +++ b/src/tests/Interop/Interop.csproj @@ -21,8 +21,6 @@ - - diff --git a/src/tests/Interop/MarshalAPI/FunctionPointer/FunctionPointer.cs b/src/tests/Interop/MarshalAPI/FunctionPointer/FunctionPointer.cs index 3dbcb6aaf3980..766a37efd00cf 100644 --- a/src/tests/Interop/MarshalAPI/FunctionPointer/FunctionPointer.cs +++ b/src/tests/Interop/MarshalAPI/FunctionPointer/FunctionPointer.cs @@ -1,8 +1,6 @@ // 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.IO; -using System.Reflection; using System.Runtime.InteropServices; using Xunit; @@ -21,8 +19,8 @@ static class FunctionPointerNative [DllImport(nameof(FunctionPointerNative))] static unsafe extern void FillOutPtr(IntPtr* p); - [DllImport(nameof(FunctionPointerNative))] - static unsafe extern void FillOutIntParameter(out IntPtr p); + [DllImport(nameof(FunctionPointerNative))] + static unsafe extern void FillOutIntParameter(out IntPtr p); } delegate void VoidDelegate(); diff --git a/src/tests/Interop/MarshalAPI/FunctionPointer/GenericFunctionPointer.cs b/src/tests/Interop/MarshalAPI/FunctionPointer/GenericFunctionPointer.cs deleted file mode 100644 index fe6d37f01a98f..0000000000000 --- a/src/tests/Interop/MarshalAPI/FunctionPointer/GenericFunctionPointer.cs +++ /dev/null @@ -1,107 +0,0 @@ -// 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.IO; -using System.Reflection; -using System.Runtime.InteropServices; -using Xunit; - -public partial class FunctionPtr -{ - public static bool CanRunGenericFunctionPointerTest => !TestLibrary.Utilities.IsMonoRuntime; - public static bool CanRunInvalidGenericFunctionPointerTest => !TestLibrary.Utilities.IsNativeAot && !TestLibrary.Utilities.IsMonoRuntime; - - [UnmanagedCallersOnly] - static int UnmanagedExportedFunction(float arg) - { - return Convert.ToInt32(arg); - } - - [UnmanagedCallersOnly] - static BlittableGeneric UnmanagedExportedFunctionBlittableGenericInt(float arg) - { - return new() { X = Convert.ToInt32(arg) }; - } - - [UnmanagedCallersOnly] - static BlittableGeneric UnmanagedExportedFunctionBlittableGenericString(float arg) - { - return new() { X = Convert.ToInt32(arg) }; - } - - class GenericCaller - { - internal static unsafe T GenericCalli(void* fnptr, U arg) - { - return ((delegate* unmanaged)fnptr)(arg); - } - - internal static unsafe BlittableGeneric WrappedGenericCalli(void* fnptr, U arg) - { - return ((delegate* unmanaged>)fnptr)(arg); - } - } - - struct BlittableGeneric - { - public int X; - } - - [ConditionalTheory(nameof(CanRunGenericFunctionPointerTest))] - [InlineData(0f)] - [InlineData(1f)] - [InlineData(-1f)] - [InlineData(42f)] - [InlineData(60f)] - public static void RunGenericFunctionPointerTest(float inVal) - { - Console.WriteLine($"Running {nameof(RunGenericFunctionPointerTest)}..."); - int outVar = 0; - int expectedValue = Convert.ToInt32(inVal); - - Console.WriteLine("Testing GenericCalli with int as the return type"); - unsafe - { - outVar = GenericCaller.GenericCalli((delegate* unmanaged)&UnmanagedExportedFunction, inVal); - } - Assert.Equal(expectedValue, outVar); - - outVar = 0; - Console.WriteLine("Testing GenericCalli with BlittableGeneric as the return type"); - unsafe - { - outVar = GenericCaller.WrappedGenericCalli((delegate* unmanaged>)&UnmanagedExportedFunctionBlittableGenericInt, inVal).X; - } - Assert.Equal(expectedValue, outVar); - - outVar = 0; - Console.WriteLine("Testing GenericCalli with BlittableGeneric as the return type"); - unsafe - { - outVar = GenericCaller.WrappedGenericCalli((delegate* unmanaged>)&UnmanagedExportedFunctionBlittableGenericString, inVal).X; - } - Assert.Equal(expectedValue, outVar); - } - - [ConditionalFact(nameof(CanRunInvalidGenericFunctionPointerTest))] - public static void RunInvalidGenericFunctionPointerTest() - { - Console.WriteLine($"Running {nameof(RunInvalidGenericFunctionPointerTest)}..."); - unsafe - { - nint fnptr = (nint)(delegate* unmanaged)&ReturnParameter; - Console.WriteLine("Testing GenericCalli with string as the parameter type"); - Assert.Throws(() => GenericCaller.GenericCalli((delegate* unmanaged)fnptr, "test")); - Console.WriteLine("Testing GenericCalli with string as the return type"); - Assert.Throws(() => GenericCaller.GenericCalli((delegate* unmanaged)fnptr, "test")); - Console.WriteLine("Testing GenericCalli with string as both the parameter and return type"); - Assert.Throws(() => GenericCaller.GenericCalli((delegate* unmanaged)fnptr, "test")); - } - } - - [UnmanagedCallersOnly] - static unsafe nint* ReturnParameter(nint* p) - { - return p; - } -}