From 01d724c1bd1ba9a21a307ee10f881be26f443dd8 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 26 Mar 2021 18:53:10 -0700 Subject: [PATCH] Add CoreCLR implementation of CallConvMemberFunction. (#47828) * Add CoreCLR implementation of CallConvMemberFunction. * Add tests and make a few fixes to get them passing. * Fix Clang build * Fix argument ordering for calling a cdecl or stdcall instance method from managed. * Update src/coreclr/dlls/mscorrc/mscorrc.rc Co-authored-by: Jan Kotas * Fix UnmanagedCallersOnly instance method calls for stdcall and cdecl. * Update issues.targets. * Fix formatting. * Add dummy param to validate param order. Rewrite traversal to second-to-last argument. * Merge implementations of call conv modopt name comparison. Use a macro-based implementation initially since it allows us to add new calling conventions by adding one line and the macro-ized code is relatively small. * Fix assignment alignment. * SAL and contracts. * Add assert * Fix UnmanagedCallersOnly parsing in the refactored parser. Co-authored-by: Jan Kotas --- src/coreclr/dlls/mscorrc/mscorrc.rc | 2 +- src/coreclr/inc/corhdr.h | 1 + src/coreclr/inc/corinfo.h | 13 +- src/coreclr/jit/importer.cpp | 44 +++- src/coreclr/jit/lclvars.cpp | 13 +- .../tools/Common/JitInterface/CorInfoImpl.cs | 33 +++ .../tools/Common/JitInterface/CorInfoTypes.cs | 5 +- src/coreclr/vm/corelib.h | 1 + src/coreclr/vm/dllimport.cpp | 6 +- src/coreclr/vm/dllimportcallback.cpp | 59 +++-- src/coreclr/vm/siginfo.cpp | 125 ++++++++--- src/coreclr/vm/siginfo.hpp | 42 ++++ src/coreclr/vm/stubgen.cpp | 12 ++ .../Runtime/CompilerServices/CallingConventions.cs | 12 ++ src/libraries/System.Runtime/ref/System.Runtime.cs | 4 + src/tests/Common/Platform/platformdefines.h | 4 +- src/tests/Interop/CMakeLists.txt | 1 - .../PInvoke/Miscellaneous/ThisCall/CMakeLists.txt | 8 - src/tests/JIT/Directed/callconv/CMakeLists.txt | 19 ++ .../CdeclMemberFunction/CdeclMemberFunctionTest.cs | 240 +++++++++++++++++++++ .../CdeclMemberFunctionTest.csproj | 13 ++ .../Directed/callconv/InstanceCallConvTest.cpp} | 18 +- .../PlatformDefaultMemberFunctionTest.cs | 240 +++++++++++++++++++++ .../PlatformDefaultMemberFunctionTest.csproj | 13 ++ .../StdCallMemberFunctionTest.cs | 239 ++++++++++++++++++++ .../StdCallMemberFunctionTest.csproj | 13 ++ .../Directed/callconv}/ThisCall/ThisCallTest.cs | 10 +- .../callconv}/ThisCall/ThisCallTest.csproj | 5 +- src/tests/issues.targets | 43 +++- 29 files changed, 1127 insertions(+), 111 deletions(-) delete mode 100644 src/tests/Interop/PInvoke/Miscellaneous/ThisCall/CMakeLists.txt create mode 100644 src/tests/JIT/Directed/callconv/CMakeLists.txt create mode 100644 src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.cs create mode 100644 src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.csproj rename src/tests/{Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp => JIT/Directed/callconv/InstanceCallConvTest.cpp} (76%) create mode 100644 src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.cs create mode 100644 src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.csproj create mode 100644 src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.cs create mode 100644 src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.csproj rename src/tests/{Interop/PInvoke/Miscellaneous => JIT/Directed/callconv}/ThisCall/ThisCallTest.cs (97%) rename src/tests/{Interop/PInvoke/Miscellaneous => JIT/Directed/callconv}/ThisCall/ThisCallTest.csproj (54%) diff --git a/src/coreclr/dlls/mscorrc/mscorrc.rc b/src/coreclr/dlls/mscorrc/mscorrc.rc index e510400..a2a7b0e 100644 --- a/src/coreclr/dlls/mscorrc/mscorrc.rc +++ b/src/coreclr/dlls/mscorrc/mscorrc.rc @@ -413,7 +413,7 @@ BEGIN IDS_INVALID_REDIM "Illegal attempt to replace or redimension a fixed or locked SafeArray." - IDS_INVALID_PINVOKE_CALLCONV "Invalid unmanaged calling convention: must be one of stdcall, cdecl, or thiscall." + IDS_INVALID_PINVOKE_CALLCONV "Unsupported unmanaged calling convention." IDS_CLASSLOAD_NSTRUCT_EXPLICIT_OFFSET "Could not load type '%1' from assembly '%2' because field '%3' was not given an explicit offset." IDS_WRONGSIZEARRAY_IN_NSTRUCT "Type could not be marshaled because the length of an embedded array instance does not match the declared length in the layout." diff --git a/src/coreclr/inc/corhdr.h b/src/coreclr/inc/corhdr.h index e3b4c90..a4c5e6d 100644 --- a/src/coreclr/inc/corhdr.h +++ b/src/coreclr/inc/corhdr.h @@ -1894,6 +1894,7 @@ typedef enum LoadHintEnum #define CMOD_CALLCONV_NAME_THISCALL "CallConvThiscall" #define CMOD_CALLCONV_NAME_FASTCALL "CallConvFastcall" #define CMOD_CALLCONV_NAME_SUPPRESSGCTRANSITION "CallConvSuppressGCTransition" +#define CMOD_CALLCONV_NAME_MEMBERFUNCTION "CallConvMemberFunction" #endif // MACROS_NOT_SUPPORTED diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index ae70256..c971d80 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -595,7 +595,7 @@ enum CorInfoHelpFunc CORINFO_HELP_JIT_PINVOKE_BEGIN, // Transition to preemptive mode before a P/Invoke, frame is the first argument CORINFO_HELP_JIT_PINVOKE_END, // Transition to cooperative mode after a P/Invoke, frame is the first argument - CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER, // Transition to cooperative mode in reverse P/Invoke prolog, frame is the first argument + CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER, // Transition to cooperative mode in reverse P/Invoke prolog, frame is the first argument CORINFO_HELP_JIT_REVERSE_PINVOKE_ENTER_TRACK_TRANSITIONS, // Transition to cooperative mode and track transitions in reverse P/Invoke prolog. CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT, // Transition to preemptive mode in reverse P/Invoke epilog, frame is the first argument CORINFO_HELP_JIT_REVERSE_PINVOKE_EXIT_TRACK_TRANSITIONS, // Transition to preemptive mode and track transitions in reverse P/Invoke prolog. @@ -711,17 +711,20 @@ enum class CorInfoCallConvExtension C, Stdcall, Thiscall, - Fastcall + Fastcall, // New calling conventions supported with the extensible calling convention encoding go here. + CMemberFunction, + StdcallMemberFunction, + FastcallMemberFunction }; #ifdef TARGET_X86 inline bool IsCallerPop(CorInfoCallConvExtension callConv) { #ifdef UNIX_X86_ABI - return callConv == CorInfoCallConvExtension::Managed || callConv == CorInfoCallConvExtension::C; + return callConv == CorInfoCallConvExtension::Managed || callConv == CorInfoCallConvExtension::C || callConv == CorInfoCallConvExtension::CMemberFunction; #else - return callConv == CorInfoCallConvExtension::C; + return callConv == CorInfoCallConvExtension::C || callConv == CorInfoCallConvExtension::CMemberFunction; #endif // UNIX_X86_ABI } #endif @@ -729,7 +732,7 @@ inline bool IsCallerPop(CorInfoCallConvExtension callConv) // Determines whether or not this calling convention is an instance method calling convention. inline bool callConvIsInstanceMethodCallConv(CorInfoCallConvExtension callConv) { - return callConv == CorInfoCallConvExtension::Thiscall; + return callConv == CorInfoCallConvExtension::Thiscall || callConv == CorInfoCallConvExtension::CMemberFunction || callConv == CorInfoCallConvExtension::StdcallMemberFunction || callConv == CorInfoCallConvExtension::FastcallMemberFunction; } // These are returned from getMethodOptions diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 005871f..e8250cd 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1311,7 +1311,41 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, { if (callConvIsInstanceMethodCallConv(srcCall->GetUnmanagedCallConv())) { +#ifdef TARGET_X86 + // The argument list has already been reversed. + // Insert the return buffer as the second-to-last node + // so it will be pushed on to the stack after the user args but before the native this arg + // as required by the native ABI. + GenTreeCall::Use* lastArg = srcCall->gtCallArgs; + if (lastArg == nullptr) + { + srcCall->gtCallArgs = gtPrependNewCallArg(destAddr, srcCall->gtCallArgs); + } + else if (srcCall->GetUnmanagedCallConv() == CorInfoCallConvExtension::Thiscall) + { + // For thiscall, the "this" parameter is not included in the argument list reversal, + // so we need to put the return buffer as the last parameter. + for (; lastArg->GetNext() != nullptr; lastArg = lastArg->GetNext()) + ; + gtInsertNewCallArgAfter(destAddr, lastArg); + } + else if (lastArg->GetNext() == nullptr) + { + srcCall->gtCallArgs = gtPrependNewCallArg(destAddr, lastArg); + } + else + { + assert(lastArg != nullptr && lastArg->GetNext() != nullptr); + GenTreeCall::Use* secondLastArg = lastArg; + lastArg = lastArg->GetNext(); + for (; lastArg->GetNext() != nullptr; secondLastArg = lastArg, lastArg = lastArg->GetNext()) + ; + assert(secondLastArg->GetNext() != nullptr); + gtInsertNewCallArgAfter(destAddr, secondLastArg); + } +#else GenTreeCall::Use* thisArg = gtInsertNewCallArgAfter(destAddr, srcCall->gtCallArgs); +#endif } else { @@ -7151,8 +7185,11 @@ void Compiler::impCheckForPInvokeCall( call->gtCallMoreFlags |= GTF_CALL_M_SUPPRESS_GC_TRANSITION; } - if (unmanagedCallConv != CorInfoCallConvExtension::C && unmanagedCallConv != CorInfoCallConvExtension::Stdcall && - unmanagedCallConv != CorInfoCallConvExtension::Thiscall) + // If we can't get the unmanaged calling convention or the calling convention is unsupported in the JIT, + // return here without inlining the native call. + if (unmanagedCallConv == CorInfoCallConvExtension::Managed || + unmanagedCallConv == CorInfoCallConvExtension::Fastcall || + unmanagedCallConv == CorInfoCallConvExtension::FastcallMemberFunction) { return; } @@ -7216,7 +7253,8 @@ void Compiler::impCheckForPInvokeCall( } // AMD64 convention is same for native and managed - if (unmanagedCallConv == CorInfoCallConvExtension::C) + if (unmanagedCallConv == CorInfoCallConvExtension::C || + unmanagedCallConv == CorInfoCallConvExtension::CMemberFunction) { call->gtFlags |= GTF_CALL_POP_ARGS; } diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 286e570..f3c1265 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -247,10 +247,13 @@ void Compiler::lvaInitTypeRef() break; case CorInfoCallConvExtension::C: case CorInfoCallConvExtension::Stdcall: + case CorInfoCallConvExtension::CMemberFunction: + case CorInfoCallConvExtension::StdcallMemberFunction: varDscInfo.Init(lvaTable, hasRetBuffArg, 0, 0); break; case CorInfoCallConvExtension::Managed: case CorInfoCallConvExtension::Fastcall: + case CorInfoCallConvExtension::FastcallMemberFunction: default: varDscInfo.Init(lvaTable, hasRetBuffArg, MAX_REG_ARG, MAX_FLOAT_REG_ARG); break; @@ -5482,9 +5485,13 @@ void Compiler::lvaAssignVirtualFrameOffsetsToArgs() // the native return buffer parameter. if (callConvIsInstanceMethodCallConv(info.compCallConv)) { - noway_assert(lvaTable[lclNum].lvIsRegArg); -#ifndef TARGET_X86 - argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs); +#ifdef TARGET_X86 + if (!lvaTable[lclNum].lvIsRegArg) + { + argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs); + } +#else + argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs); #endif // TARGET_X86 lclNum++; userArgsToSkip++; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 4142b4d..52d696e 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -564,6 +564,7 @@ namespace Internal.JitInterface } bool found = false; + bool memberFunctionVariant = false; foreach (CustomAttributeTypedArgument type in callConvArray) { if (!(type.Value is DefType defType)) @@ -572,6 +573,12 @@ namespace Internal.JitInterface if (defType.Namespace != "System.Runtime.CompilerServices") continue; + if (defType.Name == "CallConvMemberFunction") + { + memberFunctionVariant = true; + continue; + } + CorInfoCallConvExtension? callConvLocal = GetCallingConventionForCallConvType(defType); if (callConvLocal.HasValue) @@ -584,6 +591,12 @@ namespace Internal.JitInterface found = true; } } + + if (found && memberFunctionVariant) + { + callConv = GetMemberFunctionCallingConventionVariant(callConv); + } + return callConv; } @@ -595,6 +608,7 @@ namespace Internal.JitInterface return false; bool found = false; + bool memberFunctionVariant = false; foreach (EmbeddedSignatureData data in signature.GetEmbeddedSignatureData()) { if (data.kind != EmbeddedSignatureDataKind.OptionalCustomModifier) @@ -616,6 +630,11 @@ namespace Internal.JitInterface suppressGCTransition = true; continue; } + else if (defType.Name == "CallConvMemberFunction") + { + memberFunctionVariant = true; + continue; + } CorInfoCallConvExtension? callConvLocal = GetCallingConventionForCallConvType(defType); @@ -630,6 +649,11 @@ namespace Internal.JitInterface } } + if (found && memberFunctionVariant) + { + callConv = GetMemberFunctionCallingConventionVariant(callConv); + } + return found; } @@ -644,6 +668,15 @@ namespace Internal.JitInterface _ => null }; + private static CorInfoCallConvExtension GetMemberFunctionCallingConventionVariant(CorInfoCallConvExtension baseCallConv) => + baseCallConv switch + { + CorInfoCallConvExtension.C => CorInfoCallConvExtension.CMemberFunction, + CorInfoCallConvExtension.Stdcall => CorInfoCallConvExtension.StdcallMemberFunction, + CorInfoCallConvExtension.Fastcall => CorInfoCallConvExtension.FastcallMemberFunction, + var c => c + }; + private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* sig) { sig->callConv = (CorInfoCallConv)(signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index ea37d07..e787a52 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -357,8 +357,11 @@ namespace Internal.JitInterface C, Stdcall, Thiscall, - Fastcall + Fastcall, // New calling conventions supported with the extensible calling convention encoding go here. + CMemberFunction, + StdcallMemberFunction, + FastcallMemberFunction } public enum CORINFO_CALLINFO_FLAGS diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index a07cd45..71ecf17 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -777,6 +777,7 @@ DEFINE_CLASS(CALLCONV_STDCALL, CompilerServices, CallConvStd DEFINE_CLASS(CALLCONV_THISCALL, CompilerServices, CallConvThiscall) DEFINE_CLASS(CALLCONV_FASTCALL, CompilerServices, CallConvFastcall) DEFINE_CLASS(CALLCONV_SUPPRESSGCTRANSITION, CompilerServices, CallConvSuppressGCTransition) +DEFINE_CLASS(CALLCONV_MEMBERFUNCTION, CompilerServices, CallConvMemberFunction) DEFINE_CLASS_U(Interop, SafeHandle, SafeHandle) DEFINE_FIELD_U(handle, SafeHandle, m_handle) diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index 04b87ac..76bccc3 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -4037,9 +4037,9 @@ static void CreateNDirectStubAccessMetadata( } else { - if (unmgdCallConv != CorInfoCallConvExtension::Stdcall && - unmgdCallConv != CorInfoCallConvExtension::C && - unmgdCallConv != CorInfoCallConvExtension::Thiscall) + if (unmgdCallConv == CorInfoCallConvExtension::Managed || + unmgdCallConv == CorInfoCallConvExtension::Fastcall || + unmgdCallConv == CorInfoCallConvExtension::FastcallMemberFunction) { COMPlusThrow(kTypeLoadException, IDS_INVALID_PINVOKE_CALLCONV); } diff --git a/src/coreclr/vm/dllimportcallback.cpp b/src/coreclr/vm/dllimportcallback.cpp index 3791460..38314fa 100644 --- a/src/coreclr/vm/dllimportcallback.cpp +++ b/src/coreclr/vm/dllimportcallback.cpp @@ -556,22 +556,6 @@ void STDCALL LogUMTransition(UMEntryThunk* thunk) } #endif -namespace -{ - // Templated function to compute if a char string begins with a constant string. - template - bool BeginsWith(ULONG s1Len, const char* s1, const char (&s2)[S2LEN]) - { - WRAPPER_NO_CONTRACT; - - ULONG s2Len = (ULONG)S2LEN - 1; // Remove null - if (s1Len < s2Len) - return false; - - return (0 == strncmp(s1, s2, s2Len)); - } -} - bool TryGetCallingConventionFromUnmanagedCallersOnly(MethodDesc* pMD, CorInfoCallConvExtension* pCallConv) { STANDARD_VM_CONTRACT; @@ -643,34 +627,39 @@ bool TryGetCallingConventionFromUnmanagedCallersOnly(MethodDesc* pMD, CorInfoCal else { // Set WinAPI as the default - callConvLocal = MetaSig::GetDefaultUnmanagedCallingConvention(); + callConvLocal = CorInfoCallConvExtension::Managed; + + MetaSig::CallingConventionModifiers modifiers = MetaSig::CALL_CONV_MOD_NONE; + + bool foundBaseCallConv = false; + bool useMemberFunctionVariant = false; CaValue* arrayOfTypes = &namedArgs[0].val; for (ULONG i = 0; i < arrayOfTypes->arr.length; i++) { CaValue& typeNameValue = arrayOfTypes->arr[i]; - // According to ECMA-335, type name strings are UTF-8. Since we are - // looking for type names that are equivalent in ASCII and UTF-8, - // using a const char constant is acceptable. Type name strings are - // in Fully Qualified form, so we include the ',' delimiter. - if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvCdecl,")) - { - callConvLocal = CorInfoCallConvExtension::C; - } - else if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvStdcall,")) - { - callConvLocal = CorInfoCallConvExtension::Stdcall; - } - else if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvFastcall,")) + if (!MetaSig::TryApplyModOptToCallingConvention( + typeNameValue.str.pStr, + typeNameValue.str.cbStr, + MetaSig::CallConvModOptNameType::FullyQualifiedName, + &callConvLocal, + &modifiers)) { - callConvLocal = CorInfoCallConvExtension::Fastcall; - } - else if (BeginsWith(typeNameValue.str.cbStr, typeNameValue.str.pStr, "System.Runtime.CompilerServices.CallConvThiscall,")) - { - callConvLocal = CorInfoCallConvExtension::Thiscall; + // We found a second base calling convention. + return false; } } + + if (callConvLocal == CorInfoCallConvExtension::Managed) + { + callConvLocal = MetaSig::GetDefaultUnmanagedCallingConvention(); + } + + if (modifiers & MetaSig::CALL_CONV_MOD_MEMBERFUNCTION) + { + callConvLocal = MetaSig::GetMemberFunctionUnmanagedCallingConventionVariant(callConvLocal); + } } *pCallConv = callConvLocal; return true; diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp index 15c6840..6e28b00 100644 --- a/src/coreclr/vm/siginfo.cpp +++ b/src/coreclr/vm/siginfo.cpp @@ -5350,7 +5350,7 @@ MetaSig::TryGetUnmanagedCallingConventionFromModOpt( _ASSERTE(pWalk <= pSig + cSig); *callConvOut = CorInfoCallConvExtension::Managed; - bool found = false; + CallingConventionModifiers modifiers = CALL_CONV_MOD_NONE; while ((pWalk < (pSig + cSig)) && ((*pWalk == ELEMENT_TYPE_CMOD_OPT) || (*pWalk == ELEMENT_TYPE_CMOD_REQD))) { BOOL fIsOptional = (*pWalk == ELEMENT_TYPE_CMOD_OPT); @@ -5378,42 +5378,113 @@ MetaSig::TryGetUnmanagedCallingConventionFromModOpt( if (::strcmp(typeNamespace, CMOD_CALLCONV_NAMESPACE) != 0) continue; - if (::strcmp(typeName, CMOD_CALLCONV_NAME_SUPPRESSGCTRANSITION) == 0) + if (!TryApplyModOptToCallingConvention( + typeName, + ::strlen(typeName), + CallConvModOptNameType::TypeName, + callConvOut, + &modifiers)) { - *suppressGCTransitionOut = true; - continue; + // Error if there are multiple recognized base calling conventions + *errorResID = IDS_EE_MULTIPLE_CALLCONV_UNSUPPORTED; + return COR_E_INVALIDPROGRAM; } + } - const struct { - LPCSTR name; - CorInfoCallConvExtension value; - } knownCallConvs[] = { - { CMOD_CALLCONV_NAME_CDECL, CorInfoCallConvExtension::C }, - { CMOD_CALLCONV_NAME_STDCALL, CorInfoCallConvExtension::Stdcall }, - { CMOD_CALLCONV_NAME_THISCALL, CorInfoCallConvExtension::Thiscall }, - { CMOD_CALLCONV_NAME_FASTCALL, CorInfoCallConvExtension::Fastcall } }; + *suppressGCTransitionOut = ((modifiers & CALL_CONV_MOD_SUPPRESSGCTRANSITION) != 0); - for (const auto &callConv : knownCallConvs) + if (modifiers & CALL_CONV_MOD_MEMBERFUNCTION) + { + if (*callConvOut == CorInfoCallConvExtension::Managed) { - // Look for a recognized calling convention in metadata. - if (::strcmp(typeName, callConv.name) == 0) - { - // Error if there are multiple recognized calling conventions - if (found) - { - *errorResID = IDS_EE_MULTIPLE_CALLCONV_UNSUPPORTED; - return COR_E_INVALIDPROGRAM; - } - - *callConvOut = callConv.value; - found = true; - } + // In this case, the only specified calling convention is CallConvMemberFunction. + // Set *callConvOut to the default unmanaged calling convention. + *callConvOut = MetaSig::GetDefaultUnmanagedCallingConvention(); } + + *callConvOut = MetaSig::GetMemberFunctionUnmanagedCallingConventionVariant(*callConvOut); } - return found ? S_OK : S_FALSE; + return *callConvOut != CorInfoCallConvExtension::Managed ? S_OK : S_FALSE; +} + +// According to ECMA-335, type name strings are UTF-8. Since we are +// looking for type names that are equivalent in ASCII and UTF-8, +// using a const char constant is acceptable. Type name strings are +// in Fully Qualified form, so we include the ',' delimiter. +#define MAKE_FULLY_QUALIFIED_CALLCONV_TYPE_NAME_PREFIX(callConvTypeName) CMOD_CALLCONV_NAMESPACE "." callConvTypeName "," + +namespace +{ + // Templated function to compute if a char string begins with a constant string. + template + bool BeginsWith(size_t s1Len, const char* s1, const char (&s2)[S2LEN]) + { + WRAPPER_NO_CONTRACT; + + size_t s2Len = S2LEN - 1; // Remove null + + if (s1Len < s2Len) + return false; + + return (0 == strncmp(s1, s2, s2Len)); + } } +bool MetaSig::TryApplyModOptToCallingConvention( + _In_z_ LPCSTR callConvModOptName, + _In_ size_t callConvModOptNameLength, + _In_ CallConvModOptNameType nameType, + _Inout_ CorInfoCallConvExtension* pBaseCallConv, + _Inout_ CallingConventionModifiers* pCallConvModifiers) +{ + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(pBaseCallConv)); + PRECONDITION(CheckPointer(pCallConvModifiers)); + } + CONTRACTL_END; + +#define BASE_CALL_CONV(callConvModOptMetadataName, CallConvExtensionMember) \ + if (COMPARE_CALLCONV_NAME(callConvModOptName, callConvModOptMetadataName)) \ + { \ + if (*pBaseCallConv != CorInfoCallConvExtension::Managed) return false; \ + *pBaseCallConv = CorInfoCallConvExtension::CallConvExtensionMember; \ + return true; \ + } + \ +#define CALL_CONV_MODIFIER(callConvModOptMetadataName, CallConvModifiersMember) \ + if (COMPARE_CALLCONV_NAME(callConvModOptName, callConvModOptMetadataName)) \ + { \ + *pCallConvModifiers = (CallingConventionModifiers)(*pCallConvModifiers | CallConvModifiersMember); \ + return true; \ + } + +#define PARSE_CALL_CONVS \ + BASE_CALL_CONV(CMOD_CALLCONV_NAME_CDECL, C) \ + BASE_CALL_CONV(CMOD_CALLCONV_NAME_STDCALL, Stdcall) \ + BASE_CALL_CONV(CMOD_CALLCONV_NAME_THISCALL, Thiscall) \ + BASE_CALL_CONV(CMOD_CALLCONV_NAME_FASTCALL, Fastcall) \ + CALL_CONV_MODIFIER(CMOD_CALLCONV_NAME_SUPPRESSGCTRANSITION, CALL_CONV_MOD_SUPPRESSGCTRANSITION) \ + CALL_CONV_MODIFIER(CMOD_CALLCONV_NAME_MEMBERFUNCTION, CALL_CONV_MOD_MEMBERFUNCTION) \ + + if (nameType == CallConvModOptNameType::TypeName) + { +#define COMPARE_CALLCONV_NAME(userProvidedName, metadataName) ::strcmp(userProvidedName, metadataName) == 0 + PARSE_CALL_CONVS; +#undef COMPARE_CALLCONV_NAME + } + else + { + _ASSERTE(nameType == CallConvModOptNameType::FullyQualifiedName); + +#define COMPARE_CALLCONV_NAME(userProvidedName, metadataName) BeginsWith(callConvModOptNameLength, userProvidedName, MAKE_FULLY_QUALIFIED_CALLCONV_TYPE_NAME_PREFIX(metadataName)) + PARSE_CALL_CONVS; +#undef COMPARE_CALLCONV_NAME + } + return true; +} //--------------------------------------------------------------------------------------- // // Substitution from a token (TypeDef and TypeRef have empty instantiation, TypeSpec gets it from MetaData). diff --git a/src/coreclr/vm/siginfo.hpp b/src/coreclr/vm/siginfo.hpp index 718604f..ab49876 100644 --- a/src/coreclr/vm/siginfo.hpp +++ b/src/coreclr/vm/siginfo.hpp @@ -798,6 +798,30 @@ class MetaSig _Out_ bool* suppressGCTransitionOut, _Out_ UINT *errorResID); + enum CallingConventionModifiers + { + CALL_CONV_MOD_NONE = 0, + CALL_CONV_MOD_SUPPRESSGCTRANSITION = 0x1, + CALL_CONV_MOD_MEMBERFUNCTION = 0x2 + }; + + enum class CallConvModOptNameType + { + TypeName, + FullyQualifiedName + }; + + // Attempt to parse the provided calling convention name and add it to the collected modifiers and calling convention + // Returns false if the modopt is known and cannot be applied. + // This occurs when the modopt is a base calling convention and a base calling convention has already been supplied. + // Otherwise, returns true. + static bool TryApplyModOptToCallingConvention( + _In_z_ LPCSTR callConvModOptName, + _In_ size_t callConvModOptNameLength, + _In_ CallConvModOptNameType nameType, + _Inout_ CorInfoCallConvExtension* pBaseCallConv, + _Inout_ CallingConventionModifiers* pCallConvModifiers); + static CorInfoCallConvExtension GetDefaultUnmanagedCallingConvention() { #ifdef TARGET_UNIX @@ -807,6 +831,24 @@ class MetaSig #endif // !TARGET_UNIX } + static CorInfoCallConvExtension GetMemberFunctionUnmanagedCallingConventionVariant(CorInfoCallConvExtension baseCallConv) + { + switch (baseCallConv) + { + case CorInfoCallConvExtension::C: + return CorInfoCallConvExtension::CMemberFunction; + case CorInfoCallConvExtension::Stdcall: + return CorInfoCallConvExtension::StdcallMemberFunction; + case CorInfoCallConvExtension::Fastcall: + return CorInfoCallConvExtension::FastcallMemberFunction; + case CorInfoCallConvExtension::Thiscall: + return CorInfoCallConvExtension::Thiscall; + default: + _ASSERTE("Calling convention is not an unmanaged base calling convention."); + return baseCallConv; + } + } + //------------------------------------------------------------------ // Like NextArg, but return only normalized type (enums flattned to // underlying type ... diff --git a/src/coreclr/vm/stubgen.cpp b/src/coreclr/vm/stubgen.cpp index 7da6a1b..f829212 100644 --- a/src/coreclr/vm/stubgen.cpp +++ b/src/coreclr/vm/stubgen.cpp @@ -2813,6 +2813,18 @@ void ILStubLinker::SetStubTargetCallingConv(CorInfoCallConvExtension callConv) case CorInfoCallConvExtension::Fastcall: m_nativeFnSigBuilder.AddCallConvModOpt(GetToken(CoreLibBinder::GetClass(CLASS__CALLCONV_FASTCALL))); break; + case CorInfoCallConvExtension::CMemberFunction: + m_nativeFnSigBuilder.AddCallConvModOpt(GetToken(CoreLibBinder::GetClass(CLASS__CALLCONV_CDECL))); + m_nativeFnSigBuilder.AddCallConvModOpt(GetToken(CoreLibBinder::GetClass(CLASS__CALLCONV_MEMBERFUNCTION))); + break; + case CorInfoCallConvExtension::StdcallMemberFunction: + m_nativeFnSigBuilder.AddCallConvModOpt(GetToken(CoreLibBinder::GetClass(CLASS__CALLCONV_STDCALL))); + m_nativeFnSigBuilder.AddCallConvModOpt(GetToken(CoreLibBinder::GetClass(CLASS__CALLCONV_MEMBERFUNCTION))); + break; + case CorInfoCallConvExtension::FastcallMemberFunction: + m_nativeFnSigBuilder.AddCallConvModOpt(GetToken(CoreLibBinder::GetClass(CLASS__CALLCONV_FASTCALL))); + m_nativeFnSigBuilder.AddCallConvModOpt(GetToken(CoreLibBinder::GetClass(CLASS__CALLCONV_MEMBERFUNCTION))); + break; default: _ASSERTE("Unknown calling convention. Unable to encode it in the native function pointer signature."); break; diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CallingConventions.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CallingConventions.cs index 56b0a53..a501994 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CallingConventions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CallingConventions.cs @@ -30,8 +30,20 @@ namespace System.Runtime.CompilerServices /// public CallConvSuppressGCTransition() { } } + public class CallConvThiscall { public CallConvThiscall() { } } + + /// + /// Indicates that the calling convention used is the member function variant. + /// + public class CallConvMemberFunction + { + /// + /// Initializes a new instance of the class. + /// + public CallConvMemberFunction() { } + } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index a07dee6..ef66619 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -9198,6 +9198,10 @@ namespace System.Runtime.CompilerServices { public CallConvFastcall() { } } + public partial class CallConvMemberFunction + { + public CallConvMemberFunction() { } + } public partial class CallConvStdcall { public CallConvStdcall() { } diff --git a/src/tests/Common/Platform/platformdefines.h b/src/tests/Common/Platform/platformdefines.h index 3fc083d..35781ef 100644 --- a/src/tests/Common/Platform/platformdefines.h +++ b/src/tests/Common/Platform/platformdefines.h @@ -118,10 +118,12 @@ typedef unsigned int ULONG, *PULONG; #define __stdcall __attribute__((stdcall)) #define _cdecl __attribute__((cdecl)) #define __cdecl __attribute__((cdecl)) +#define __thiscall __attribute__((thiscall)) #else #define __stdcall #define _cdecl #define __cdecl +#define __thiscall #endif #endif @@ -156,7 +158,7 @@ typedef int error_t; typedef void* LPVOID; typedef unsigned char BYTE; typedef WCHAR OLECHAR; -typedef double DATE; +typedef double DATE; typedef DWORD LCID; #endif diff --git a/src/tests/Interop/CMakeLists.txt b/src/tests/Interop/CMakeLists.txt index 172214b..a39791f 100644 --- a/src/tests/Interop/CMakeLists.txt +++ b/src/tests/Interop/CMakeLists.txt @@ -28,7 +28,6 @@ add_subdirectory(PInvoke/SizeParamIndex/ReversePInvoke/PassingByRef) add_subdirectory(PInvoke/Array/MarshalArrayAsField/LPArrayNative) add_subdirectory(PInvoke/Array/MarshalArrayAsParam/LPArrayNative) add_subdirectory(PInvoke/Miscellaneous/HandleRef) -add_subdirectory(PInvoke/Miscellaneous/ThisCall) add_subdirectory(PInvoke/Miscellaneous/MultipleAssembliesWithSamePInvoke) add_subdirectory(PInvoke/CriticalHandles) add_subdirectory(PInvoke/Generics) diff --git a/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/CMakeLists.txt b/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/CMakeLists.txt deleted file mode 100644 index 305104d..0000000 --- a/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ - -project (ThisCallNative) -include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") -set(SOURCES - ThisCallNative.cpp -) -add_library (ThisCallNative SHARED ${SOURCES}) -install (TARGETS ThisCallNative DESTINATION bin) diff --git a/src/tests/JIT/Directed/callconv/CMakeLists.txt b/src/tests/JIT/Directed/callconv/CMakeLists.txt new file mode 100644 index 0000000..97d95eb --- /dev/null +++ b/src/tests/JIT/Directed/callconv/CMakeLists.txt @@ -0,0 +1,19 @@ +include_directories(${INC_PLATFORM_DIR}) + +set(SOURCES + InstanceCallConvTest.cpp +) + +add_library (ThisCallNative SHARED ${SOURCES}) +target_compile_definitions(ThisCallNative PRIVATE INSTANCE_CALLCONV=__thiscall) + +add_library (StdCallMemberFunctionNative SHARED ${SOURCES}) +target_compile_definitions(StdCallMemberFunctionNative PRIVATE INSTANCE_CALLCONV=__stdcall) + +add_library (CdeclMemberFunctionNative SHARED ${SOURCES}) +target_compile_definitions(CdeclMemberFunctionNative PRIVATE INSTANCE_CALLCONV=__cdecl) + +add_library (PlatformDefaultMemberFunctionNative SHARED ${SOURCES}) +target_compile_definitions(PlatformDefaultMemberFunctionNative PRIVATE INSTANCE_CALLCONV=STDMETHODCALLTYPE) + +install (TARGETS ThisCallNative DESTINATION bin) diff --git a/src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.cs b/src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.cs new file mode 100644 index 0000000..746150b --- /dev/null +++ b/src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.cs @@ -0,0 +1,240 @@ +// 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.Reflection; +using System.Text; +using TestLibrary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +unsafe class CdeclMemberFunctionNative +{ + public struct C + { + public struct VtableLayout + { + public delegate* unmanaged[Cdecl, MemberFunction] getSize; + public delegate* unmanaged[Cdecl, MemberFunction] getWidth; + public delegate* unmanaged[Cdecl, MemberFunction] getHeightAsInt; + public delegate* unmanaged[Cdecl, MemberFunction] getE; + public delegate* unmanaged[Cdecl, MemberFunction] getWidthAsLong; + } + + public VtableLayout* vtable; + public E dummy; + public float width; + public float height; + } + + public struct SizeF + { + public float width; + public float height; + } + + public struct Width + { + public float width; + } + + public struct IntWrapper + { + public int i; + } + + public enum E : uint + { + Value = 42 + } + + [DllImport(nameof(CdeclMemberFunctionNative))] + public static extern C* CreateInstanceOfC(float width, float height); + + [DllImport(nameof(CdeclMemberFunctionNative))] + public static extern SizeF GetSizeFromManaged(C* c); + [DllImport(nameof(CdeclMemberFunctionNative))] + public static extern Width GetWidthFromManaged(C* c); + [DllImport(nameof(CdeclMemberFunctionNative))] + public static extern IntWrapper GetHeightAsIntFromManaged(C* c); + [DllImport(nameof(CdeclMemberFunctionNative))] + public static extern E GetEFromManaged(C* c); + [DllImport(nameof(CdeclMemberFunctionNative))] + public static extern CLong GetWidthAsLongFromManaged(C* c); +} + +unsafe class CdeclMemberFunctionTest +{ + public static int Main(string[] args) + { + try + { + float width = 1.0f; + float height = 2.0f; + CdeclMemberFunctionNative.C* instance = CdeclMemberFunctionNative.CreateInstanceOfC(width, height); + Test8ByteHFA(instance); + Test4ByteHFA(instance); + Test4ByteNonHFA(instance); + TestEnum(instance); + TestCLong(instance); + Test8ByteHFAUnmanagedCallersOnly(); + Test4ByteHFAUnmanagedCallersOnly(); + Test4ByteNonHFAUnmanagedCallersOnly(); + TestEnumUnmanagedCallersOnly(); + TestCLongUnmanagedCallersOnly(); + } + catch (System.Exception ex) + { + Console.WriteLine(ex); + return 101; + } + return 100; + } + + private static void Test8ByteHFA(CdeclMemberFunctionNative.C* instance) + { + CdeclMemberFunctionNative.SizeF result = instance->vtable->getSize(instance, 1234); + + Assert.AreEqual(instance->width, result.width); + Assert.AreEqual(instance->height, result.height); + } + + private static void Test4ByteHFA(CdeclMemberFunctionNative.C* instance) + { + CdeclMemberFunctionNative.Width result = instance->vtable->getWidth(instance); + + Assert.AreEqual(instance->width, result.width); + } + + private static void Test4ByteNonHFA(CdeclMemberFunctionNative.C* instance) + { + CdeclMemberFunctionNative.IntWrapper result = instance->vtable->getHeightAsInt(instance); + + Assert.AreEqual((int)instance->height, result.i); + } + + private static void TestEnum(CdeclMemberFunctionNative.C* instance) + { + CdeclMemberFunctionNative.E result = instance->vtable->getE(instance); + + Assert.AreEqual(instance->dummy, result); + } + + private static void TestCLong(CdeclMemberFunctionNative.C* instance) + { + CLong result = instance->vtable->getWidthAsLong(instance); + + Assert.AreEqual((nint)instance->width, result.Value); + } + + private static void Test8ByteHFAUnmanagedCallersOnly() + { + CdeclMemberFunctionNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); + CdeclMemberFunctionNative.SizeF result = CdeclMemberFunctionNative.GetSizeFromManaged(&c); + + Assert.AreEqual(c.width, result.width); + Assert.AreEqual(c.height, result.height); + } + + private static void Test4ByteHFAUnmanagedCallersOnly() + { + CdeclMemberFunctionNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); + CdeclMemberFunctionNative.Width result = CdeclMemberFunctionNative.GetWidthFromManaged(&c); + + Assert.AreEqual(c.width, result.width); + } + + private static void Test4ByteNonHFAUnmanagedCallersOnly() + { + CdeclMemberFunctionNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); + CdeclMemberFunctionNative.IntWrapper result = CdeclMemberFunctionNative.GetHeightAsIntFromManaged(&c); + + Assert.AreEqual((int)c.height, result.i); + } + + private static void TestEnumUnmanagedCallersOnly() + { + CdeclMemberFunctionNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); + CdeclMemberFunctionNative.E result = CdeclMemberFunctionNative.GetEFromManaged(&c); + + Assert.AreEqual(c.dummy, result); + } + + private static void TestCLongUnmanagedCallersOnly() + { + CdeclMemberFunctionNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); + CLong result = CdeclMemberFunctionNative.GetWidthAsLongFromManaged(&c); + + Assert.AreEqual((nint)c.width, result.Value); + } + + private static CdeclMemberFunctionNative.C CreateCWithUnmanagedCallersOnlyVTable(float width, float height) + { + return new CdeclMemberFunctionNative.C + { + vtable = UnmanagedCallersOnlyVtable, + dummy = CdeclMemberFunctionNative.E.Value, + width = width, + height = height + }; + } + + private static CdeclMemberFunctionNative.C.VtableLayout* unmanagedCallersOnlyVtable; + + private static CdeclMemberFunctionNative.C.VtableLayout* UnmanagedCallersOnlyVtable + { + get + { + if (unmanagedCallersOnlyVtable == null) + { + unmanagedCallersOnlyVtable = (CdeclMemberFunctionNative.C.VtableLayout*)Marshal.AllocHGlobal(sizeof(CdeclMemberFunctionNative.C.VtableLayout)); + unmanagedCallersOnlyVtable->getSize = &GetSize; + unmanagedCallersOnlyVtable->getWidth = &GetWidth; + unmanagedCallersOnlyVtable->getHeightAsInt = &GetHeightAsInt; + unmanagedCallersOnlyVtable->getE = &GetE; + unmanagedCallersOnlyVtable->getWidthAsLong = &GetWidthAsLong; + } + return unmanagedCallersOnlyVtable; + } + } + + [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvCdecl), typeof(CallConvMemberFunction)})] + private static CdeclMemberFunctionNative.SizeF GetSize(CdeclMemberFunctionNative.C* c, int unused) + { + return new CdeclMemberFunctionNative.SizeF + { + width = c->width, + height = c->height + }; + } + + [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvCdecl), typeof(CallConvMemberFunction)})] + private static CdeclMemberFunctionNative.Width GetWidth(CdeclMemberFunctionNative.C* c) + { + return new CdeclMemberFunctionNative.Width + { + width = c->width + }; + } + + [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvCdecl), typeof(CallConvMemberFunction)})] + private static CdeclMemberFunctionNative.IntWrapper GetHeightAsInt(CdeclMemberFunctionNative.C* c) + { + return new CdeclMemberFunctionNative.IntWrapper + { + i = (int)c->height + }; + } + + [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvCdecl), typeof(CallConvMemberFunction)})] + private static CdeclMemberFunctionNative.E GetE(CdeclMemberFunctionNative.C* c) + { + return c->dummy; + } + + [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvCdecl), typeof(CallConvMemberFunction)})] + private static CLong GetWidthAsLong(CdeclMemberFunctionNative.C* c) + { + return new CLong((nint)c->width); + } +} diff --git a/src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.csproj b/src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.csproj new file mode 100644 index 0000000..3d1c4a3 --- /dev/null +++ b/src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.csproj @@ -0,0 +1,13 @@ + + + Exe + true + + + + + + + + + diff --git a/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp b/src/tests/JIT/Directed/callconv/InstanceCallConvTest.cpp similarity index 76% rename from src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp rename to src/tests/JIT/Directed/callconv/InstanceCallConvTest.cpp index 6c70fc5..129e81c 100644 --- a/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp +++ b/src/tests/JIT/Directed/callconv/InstanceCallConvTest.cpp @@ -2,9 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. #include -#include #include +#ifndef INSTANCE_CALLCONV +#error The INSTANCE_CALLCONV define must be defined as the calling convention to use for the instance methods. +#endif + struct SizeF { float width; @@ -38,33 +41,32 @@ public: height(height) {} - virtual SizeF GetSize() + virtual SizeF INSTANCE_CALLCONV GetSize(int) { return {width, height}; } - virtual Width GetWidth() + virtual Width INSTANCE_CALLCONV GetWidth() { return {width}; } - virtual IntWrapper GetHeightAsInt() + virtual IntWrapper INSTANCE_CALLCONV GetHeightAsInt() { return {(int)height}; } - virtual E GetE() + virtual E INSTANCE_CALLCONV GetE() { return dummy; } - virtual long GetWidthAsLong() + virtual long INSTANCE_CALLCONV GetWidthAsLong() { return (long)width; } }; - extern "C" DLL_EXPORT C* STDMETHODCALLTYPE CreateInstanceOfC(float width, float height) { return new C(width, height); @@ -72,7 +74,7 @@ extern "C" DLL_EXPORT C* STDMETHODCALLTYPE CreateInstanceOfC(float width, float extern "C" DLL_EXPORT SizeF STDMETHODCALLTYPE GetSizeFromManaged(C* c) { - return c->GetSize(); + return c->GetSize(9876); } extern "C" DLL_EXPORT Width STDMETHODCALLTYPE GetWidthFromManaged(C* c) diff --git a/src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.cs b/src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.cs new file mode 100644 index 0000000..bb1429a --- /dev/null +++ b/src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.cs @@ -0,0 +1,240 @@ +// 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.Reflection; +using System.Text; +using TestLibrary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +unsafe class PlatformDefaultMemberFunctionNative +{ + public struct C + { + public struct VtableLayout + { + public delegate* unmanaged[MemberFunction] getSize; + public delegate* unmanaged[MemberFunction] getWidth; + public delegate* unmanaged[MemberFunction] getHeightAsInt; + public delegate* unmanaged[MemberFunction] getE; + public delegate* unmanaged[MemberFunction] getWidthAsLong; + } + + public VtableLayout* vtable; + public E dummy; + public float width; + public float height; + } + + public struct SizeF + { + public float width; + public float height; + } + + public struct Width + { + public float width; + } + + public struct IntWrapper + { + public int i; + } + + public enum E : uint + { + Value = 42 + } + + [DllImport(nameof(PlatformDefaultMemberFunctionNative))] + public static extern C* CreateInstanceOfC(float width, float height); + + [DllImport(nameof(PlatformDefaultMemberFunctionNative))] + public static extern SizeF GetSizeFromManaged(C* c); + [DllImport(nameof(PlatformDefaultMemberFunctionNative))] + public static extern Width GetWidthFromManaged(C* c); + [DllImport(nameof(PlatformDefaultMemberFunctionNative))] + public static extern IntWrapper GetHeightAsIntFromManaged(C* c); + [DllImport(nameof(PlatformDefaultMemberFunctionNative))] + public static extern E GetEFromManaged(C* c); + [DllImport(nameof(PlatformDefaultMemberFunctionNative))] + public static extern CLong GetWidthAsLongFromManaged(C* c); +} + +unsafe class PlatformDefaultMemberFunctionTest +{ + public static int Main(string[] args) + { + try + { + float width = 1.0f; + float height = 2.0f; + PlatformDefaultMemberFunctionNative.C* instance = PlatformDefaultMemberFunctionNative.CreateInstanceOfC(width, height); + Test8ByteHFA(instance); + Test4ByteHFA(instance); + Test4ByteNonHFA(instance); + TestEnum(instance); + TestCLong(instance); + Test8ByteHFAUnmanagedCallersOnly(); + Test4ByteHFAUnmanagedCallersOnly(); + Test4ByteNonHFAUnmanagedCallersOnly(); + TestEnumUnmanagedCallersOnly(); + TestCLongUnmanagedCallersOnly(); + } + catch (System.Exception ex) + { + Console.WriteLine(ex); + return 101; + } + return 100; + } + + private static void Test8ByteHFA(PlatformDefaultMemberFunctionNative.C* instance) + { + PlatformDefaultMemberFunctionNative.SizeF result = instance->vtable->getSize(instance, 1234); + + Assert.AreEqual(instance->width, result.width); + Assert.AreEqual(instance->height, result.height); + } + + private static void Test4ByteHFA(PlatformDefaultMemberFunctionNative.C* instance) + { + PlatformDefaultMemberFunctionNative.Width result = instance->vtable->getWidth(instance); + + Assert.AreEqual(instance->width, result.width); + } + + private static void Test4ByteNonHFA(PlatformDefaultMemberFunctionNative.C* instance) + { + PlatformDefaultMemberFunctionNative.IntWrapper result = instance->vtable->getHeightAsInt(instance); + + Assert.AreEqual((int)instance->height, result.i); + } + + private static void TestEnum(PlatformDefaultMemberFunctionNative.C* instance) + { + PlatformDefaultMemberFunctionNative.E result = instance->vtable->getE(instance); + + Assert.AreEqual(instance->dummy, result); + } + + private static void TestCLong(PlatformDefaultMemberFunctionNative.C* instance) + { + CLong result = instance->vtable->getWidthAsLong(instance); + + Assert.AreEqual((nint)instance->width, result.Value); + } + + private static void Test8ByteHFAUnmanagedCallersOnly() + { + PlatformDefaultMemberFunctionNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); + PlatformDefaultMemberFunctionNative.SizeF result = PlatformDefaultMemberFunctionNative.GetSizeFromManaged(&c); + + Assert.AreEqual(c.width, result.width); + Assert.AreEqual(c.height, result.height); + } + + private static void Test4ByteHFAUnmanagedCallersOnly() + { + PlatformDefaultMemberFunctionNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); + PlatformDefaultMemberFunctionNative.Width result = PlatformDefaultMemberFunctionNative.GetWidthFromManaged(&c); + + Assert.AreEqual(c.width, result.width); + } + + private static void Test4ByteNonHFAUnmanagedCallersOnly() + { + PlatformDefaultMemberFunctionNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); + PlatformDefaultMemberFunctionNative.IntWrapper result = PlatformDefaultMemberFunctionNative.GetHeightAsIntFromManaged(&c); + + Assert.AreEqual((int)c.height, result.i); + } + + private static void TestEnumUnmanagedCallersOnly() + { + PlatformDefaultMemberFunctionNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); + PlatformDefaultMemberFunctionNative.E result = PlatformDefaultMemberFunctionNative.GetEFromManaged(&c); + + Assert.AreEqual(c.dummy, result); + } + + private static void TestCLongUnmanagedCallersOnly() + { + PlatformDefaultMemberFunctionNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); + CLong result = PlatformDefaultMemberFunctionNative.GetWidthAsLongFromManaged(&c); + + Assert.AreEqual((nint)c.width, result.Value); + } + + private static PlatformDefaultMemberFunctionNative.C CreateCWithUnmanagedCallersOnlyVTable(float width, float height) + { + return new PlatformDefaultMemberFunctionNative.C + { + vtable = UnmanagedCallersOnlyVtable, + dummy = PlatformDefaultMemberFunctionNative.E.Value, + width = width, + height = height + }; + } + + private static PlatformDefaultMemberFunctionNative.C.VtableLayout* unmanagedCallersOnlyVtable; + + private static PlatformDefaultMemberFunctionNative.C.VtableLayout* UnmanagedCallersOnlyVtable + { + get + { + if (unmanagedCallersOnlyVtable == null) + { + unmanagedCallersOnlyVtable = (PlatformDefaultMemberFunctionNative.C.VtableLayout*)Marshal.AllocHGlobal(sizeof(PlatformDefaultMemberFunctionNative.C.VtableLayout)); + unmanagedCallersOnlyVtable->getSize = &GetSize; + unmanagedCallersOnlyVtable->getWidth = &GetWidth; + unmanagedCallersOnlyVtable->getHeightAsInt = &GetHeightAsInt; + unmanagedCallersOnlyVtable->getE = &GetE; + unmanagedCallersOnlyVtable->getWidthAsLong = &GetWidthAsLong; + } + return unmanagedCallersOnlyVtable; + } + } + + [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvMemberFunction)})] + private static PlatformDefaultMemberFunctionNative.SizeF GetSize(PlatformDefaultMemberFunctionNative.C* c, int unused) + { + return new PlatformDefaultMemberFunctionNative.SizeF + { + width = c->width, + height = c->height + }; + } + + [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvMemberFunction)})] + private static PlatformDefaultMemberFunctionNative.Width GetWidth(PlatformDefaultMemberFunctionNative.C* c) + { + return new PlatformDefaultMemberFunctionNative.Width + { + width = c->width + }; + } + + [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvMemberFunction)})] + private static PlatformDefaultMemberFunctionNative.IntWrapper GetHeightAsInt(PlatformDefaultMemberFunctionNative.C* c) + { + return new PlatformDefaultMemberFunctionNative.IntWrapper + { + i = (int)c->height + }; + } + + [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvMemberFunction)})] + private static PlatformDefaultMemberFunctionNative.E GetE(PlatformDefaultMemberFunctionNative.C* c) + { + return c->dummy; + } + + [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvMemberFunction)})] + private static CLong GetWidthAsLong(PlatformDefaultMemberFunctionNative.C* c) + { + return new CLong((nint)c->width); + } +} diff --git a/src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.csproj b/src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.csproj new file mode 100644 index 0000000..3d1c4a3 --- /dev/null +++ b/src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.csproj @@ -0,0 +1,13 @@ + + + Exe + true + + + + + + + + + diff --git a/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.cs b/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.cs new file mode 100644 index 0000000..f3480f0 --- /dev/null +++ b/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.cs @@ -0,0 +1,239 @@ +// 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.Reflection; +using System.Text; +using TestLibrary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +unsafe class StdCallMemberFunctionNative +{ + public struct C + { + public struct VtableLayout + { + public delegate* unmanaged[Stdcall, MemberFunction] getSize; + public delegate* unmanaged[Stdcall, MemberFunction] getWidth; + public delegate* unmanaged[Stdcall, MemberFunction] getHeightAsInt; + public delegate* unmanaged[Stdcall, MemberFunction] getE; + public delegate* unmanaged[Stdcall, MemberFunction] getWidthAsLong; + } + + public VtableLayout* vtable; + public E dummy; + public float width; + public float height; + } + + public struct SizeF + { + public float width; + public float height; + } + + public struct Width + { + public float width; + } + + public struct IntWrapper + { + public int i; + } + + public enum E : uint + { + Value = 42 + } + + [DllImport(nameof(StdCallMemberFunctionNative))] + public static extern C* CreateInstanceOfC(float width, float height); + + [DllImport(nameof(StdCallMemberFunctionNative))] + public static extern SizeF GetSizeFromManaged(C* c); + [DllImport(nameof(StdCallMemberFunctionNative))] + public static extern Width GetWidthFromManaged(C* c); + [DllImport(nameof(StdCallMemberFunctionNative))] + public static extern IntWrapper GetHeightAsIntFromManaged(C* c); + [DllImport(nameof(StdCallMemberFunctionNative))] + public static extern E GetEFromManaged(C* c); + [DllImport(nameof(StdCallMemberFunctionNative))] + public static extern CLong GetWidthAsLongFromManaged(C* c); +} + +unsafe class StdCallMemberFunctionTest +{ + public static int Main(string[] args) + { + try + { + float width = 1.0f; + float height = 2.0f; + StdCallMemberFunctionNative.C* instance = StdCallMemberFunctionNative.CreateInstanceOfC(width, height); + Test8ByteHFA(instance); + Test4ByteHFA(instance); + Test4ByteNonHFA(instance); + TestEnum(instance); + TestCLong(instance); + Test8ByteHFAUnmanagedCallersOnly(); + Test4ByteHFAUnmanagedCallersOnly(); + Test4ByteNonHFAUnmanagedCallersOnly(); + TestEnumUnmanagedCallersOnly(); + TestCLongUnmanagedCallersOnly(); + } + catch (System.Exception ex) + { + Console.WriteLine(ex); + return 101; + } + return 100; + } + + private static void Test8ByteHFA(StdCallMemberFunctionNative.C* instance) + { + StdCallMemberFunctionNative.SizeF result = instance->vtable->getSize(instance, 1234); + + Assert.AreEqual(instance->width, result.width); + Assert.AreEqual(instance->height, result.height); + } + + private static void Test4ByteHFA(StdCallMemberFunctionNative.C* instance) + { + StdCallMemberFunctionNative.Width result = instance->vtable->getWidth(instance); + + Assert.AreEqual(instance->width, result.width); + } + + private static void Test4ByteNonHFA(StdCallMemberFunctionNative.C* instance) + { + StdCallMemberFunctionNative.IntWrapper result = instance->vtable->getHeightAsInt(instance); + + Assert.AreEqual((int)instance->height, result.i); + } + + private static void TestEnum(StdCallMemberFunctionNative.C* instance) + { + StdCallMemberFunctionNative.E result = instance->vtable->getE(instance); + + Assert.AreEqual(instance->dummy, result); + } + + private static void TestCLong(StdCallMemberFunctionNative.C* instance) + { + CLong result = instance->vtable->getWidthAsLong(instance); + + Assert.AreEqual((nint)instance->width, result.Value); + } + private static void Test8ByteHFAUnmanagedCallersOnly() + { + StdCallMemberFunctionNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); + StdCallMemberFunctionNative.SizeF result = StdCallMemberFunctionNative.GetSizeFromManaged(&c); + + Assert.AreEqual(c.width, result.width); + Assert.AreEqual(c.height, result.height); + } + + private static void Test4ByteHFAUnmanagedCallersOnly() + { + StdCallMemberFunctionNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); + StdCallMemberFunctionNative.Width result = StdCallMemberFunctionNative.GetWidthFromManaged(&c); + + Assert.AreEqual(c.width, result.width); + } + + private static void Test4ByteNonHFAUnmanagedCallersOnly() + { + StdCallMemberFunctionNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); + StdCallMemberFunctionNative.IntWrapper result = StdCallMemberFunctionNative.GetHeightAsIntFromManaged(&c); + + Assert.AreEqual((int)c.height, result.i); + } + + private static void TestEnumUnmanagedCallersOnly() + { + StdCallMemberFunctionNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); + StdCallMemberFunctionNative.E result = StdCallMemberFunctionNative.GetEFromManaged(&c); + + Assert.AreEqual(c.dummy, result); + } + + private static void TestCLongUnmanagedCallersOnly() + { + StdCallMemberFunctionNative.C c = CreateCWithUnmanagedCallersOnlyVTable(2.0f, 3.0f); + CLong result = StdCallMemberFunctionNative.GetWidthAsLongFromManaged(&c); + + Assert.AreEqual((nint)c.width, result.Value); + } + + private static StdCallMemberFunctionNative.C CreateCWithUnmanagedCallersOnlyVTable(float width, float height) + { + return new StdCallMemberFunctionNative.C + { + vtable = UnmanagedCallersOnlyVtable, + dummy = StdCallMemberFunctionNative.E.Value, + width = width, + height = height + }; + } + + private static StdCallMemberFunctionNative.C.VtableLayout* unmanagedCallersOnlyVtable; + + private static StdCallMemberFunctionNative.C.VtableLayout* UnmanagedCallersOnlyVtable + { + get + { + if (unmanagedCallersOnlyVtable == null) + { + unmanagedCallersOnlyVtable = (StdCallMemberFunctionNative.C.VtableLayout*)Marshal.AllocHGlobal(sizeof(StdCallMemberFunctionNative.C.VtableLayout)); + unmanagedCallersOnlyVtable->getSize = &GetSize; + unmanagedCallersOnlyVtable->getWidth = &GetWidth; + unmanagedCallersOnlyVtable->getHeightAsInt = &GetHeightAsInt; + unmanagedCallersOnlyVtable->getE = &GetE; + unmanagedCallersOnlyVtable->getWidthAsLong = &GetWidthAsLong; + } + return unmanagedCallersOnlyVtable; + } + } + + [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvStdcall), typeof(CallConvMemberFunction)})] + private static StdCallMemberFunctionNative.SizeF GetSize(StdCallMemberFunctionNative.C* c, int unused) + { + return new StdCallMemberFunctionNative.SizeF + { + width = c->width, + height = c->height + }; + } + + [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvStdcall), typeof(CallConvMemberFunction)})] + private static StdCallMemberFunctionNative.Width GetWidth(StdCallMemberFunctionNative.C* c) + { + return new StdCallMemberFunctionNative.Width + { + width = c->width + }; + } + + [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvStdcall), typeof(CallConvMemberFunction)})] + private static StdCallMemberFunctionNative.IntWrapper GetHeightAsInt(StdCallMemberFunctionNative.C* c) + { + return new StdCallMemberFunctionNative.IntWrapper + { + i = (int)c->height + }; + } + + [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvStdcall), typeof(CallConvMemberFunction)})] + private static StdCallMemberFunctionNative.E GetE(StdCallMemberFunctionNative.C* c) + { + return c->dummy; + } + + [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvStdcall), typeof(CallConvMemberFunction)})] + private static CLong GetWidthAsLong(StdCallMemberFunctionNative.C* c) + { + return new CLong((nint)c->width); + } +} diff --git a/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.csproj b/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.csproj new file mode 100644 index 0000000..3d1c4a3 --- /dev/null +++ b/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.csproj @@ -0,0 +1,13 @@ + + + Exe + true + + + + + + + + + diff --git a/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs b/src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.cs similarity index 97% rename from src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs rename to src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.cs index 0509ffe..16ef813 100644 --- a/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs +++ b/src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.cs @@ -49,7 +49,7 @@ unsafe class ThisCallNative } [UnmanagedFunctionPointer(CallingConvention.ThisCall)] - public delegate SizeF GetSizeFn(C* c); + public delegate SizeF GetSizeFn(C* c, int unused); [UnmanagedFunctionPointer(CallingConvention.ThisCall)] public delegate Width GetWidthFn(C* c); [UnmanagedFunctionPointer(CallingConvention.ThisCall)] @@ -113,7 +113,7 @@ unsafe class ThisCallTest { ThisCallNative.GetSizeFn callback = Marshal.GetDelegateForFunctionPointer(instance->vtable->getSize); - ThisCallNative.SizeF result = callback(instance); + ThisCallNative.SizeF result = callback(instance, 1234); Assert.AreEqual(instance->width, result.width); Assert.AreEqual(instance->height, result.height); @@ -269,7 +269,7 @@ unsafe class ThisCallTest { managedVtable = (ThisCallNative.C.VtableLayout*)Marshal.AllocHGlobal(sizeof(ThisCallNative.C.VtableLayout)); managedVtable->getSize = Marshal.GetFunctionPointerForDelegate( - (ThisCallNative.GetSizeFn)((ThisCallNative.C* c) => new ThisCallNative.SizeF { width = c->width, height = c->height} )); + (ThisCallNative.GetSizeFn)((ThisCallNative.C* c, int unused) => new ThisCallNative.SizeF { width = c->width, height = c->height} )); managedVtable->getWidth = Marshal.GetFunctionPointerForDelegate( (ThisCallNative.GetWidthFn)((ThisCallNative.C* c) => new ThisCallNative.Width { width = c->width} )); managedVtable->getHeightAsInt = Marshal.GetFunctionPointerForDelegate( @@ -292,7 +292,7 @@ unsafe class ThisCallTest if (unmanagedCallersOnlyVtable == null) { unmanagedCallersOnlyVtable = (ThisCallNative.C.VtableLayout*)Marshal.AllocHGlobal(sizeof(ThisCallNative.C.VtableLayout)); - unmanagedCallersOnlyVtable->getSize = (IntPtr)(delegate* unmanaged[Thiscall])&GetSize; + unmanagedCallersOnlyVtable->getSize = (IntPtr)(delegate* unmanaged[Thiscall])&GetSize; unmanagedCallersOnlyVtable->getWidth = (IntPtr)(delegate* unmanaged[Thiscall])&GetWidth; unmanagedCallersOnlyVtable->getHeightAsInt = (IntPtr)(delegate* unmanaged[Thiscall])&GetHeightAsInt; unmanagedCallersOnlyVtable->getE = (IntPtr)(delegate* unmanaged[Thiscall])&GetE; @@ -303,7 +303,7 @@ unsafe class ThisCallTest } [UnmanagedCallersOnly(CallConvs = new [] {typeof(CallConvThiscall)})] - private static ThisCallNative.SizeF GetSize(ThisCallNative.C* c) + private static ThisCallNative.SizeF GetSize(ThisCallNative.C* c, int unused) { return new ThisCallNative.SizeF { diff --git a/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.csproj b/src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.csproj similarity index 54% rename from src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.csproj rename to src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.csproj index e8b5bdd..3d1c4a3 100644 --- a/src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.csproj +++ b/src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.csproj @@ -4,9 +4,10 @@ true - + - + + diff --git a/src/tests/issues.targets b/src/tests/issues.targets index dede30e..78d51c1 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -410,8 +410,17 @@ https://github.com/dotnet/runtime/issues/12299 - - Thiscall not supported on Windows ARM32. + + Native member function calling conventions not supported on Windows ARM32. + + + Native member function calling conventions not supported on Windows ARM32. + + + Native member function calling conventions not supported on Windows ARM32. + + + Native member function calling conventions not supported on Windows ARM32. https://github.com/dotnet/runtime/issues/12979 @@ -2709,9 +2718,6 @@ https://github.com/dotnet/runtime/issues/41519 - - https://github.com/dotnet/runtime/issues/41519 - https://github.com/dotnet/runtime/issues/41519 @@ -2742,6 +2748,18 @@ https://github.com/dotnet/runtime/issues/41472 + + https://github.com/dotnet/runtime/issues/41519 + + + https://github.com/dotnet/runtime/issues/41519 + + + https://github.com/dotnet/runtime/issues/41519 + + + https://github.com/dotnet/runtime/issues/41519 + https://github.com/dotnet/runtime/issues/41472 @@ -3258,15 +3276,24 @@ needs triage - - needs triage - needs triage needs triage + + needs triage + + + needs triage + + + needs triage + + + needs triage + https://github.com/dotnet/runtime/issues/44648 -- 2.7.4