Add CoreCLR implementation of CallConvMemberFunction. (#47828)
authorJeremy Koritzinsky <jekoritz@microsoft.com>
Sat, 27 Mar 2021 01:53:10 +0000 (18:53 -0700)
committerGitHub <noreply@github.com>
Sat, 27 Mar 2021 01:53:10 +0000 (01:53 +0000)
* 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 <jkotas@microsoft.com>
* 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 <jkotas@microsoft.com>
29 files changed:
src/coreclr/dlls/mscorrc/mscorrc.rc
src/coreclr/inc/corhdr.h
src/coreclr/inc/corinfo.h
src/coreclr/jit/importer.cpp
src/coreclr/jit/lclvars.cpp
src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs
src/coreclr/vm/corelib.h
src/coreclr/vm/dllimport.cpp
src/coreclr/vm/dllimportcallback.cpp
src/coreclr/vm/siginfo.cpp
src/coreclr/vm/siginfo.hpp
src/coreclr/vm/stubgen.cpp
src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/CallingConventions.cs
src/libraries/System.Runtime/ref/System.Runtime.cs
src/tests/Common/Platform/platformdefines.h
src/tests/Interop/CMakeLists.txt
src/tests/Interop/PInvoke/Miscellaneous/ThisCall/CMakeLists.txt [deleted file]
src/tests/JIT/Directed/callconv/CMakeLists.txt [new file with mode: 0644]
src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.cs [new file with mode: 0644]
src/tests/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest.csproj [new file with mode: 0644]
src/tests/JIT/Directed/callconv/InstanceCallConvTest.cpp [moved from src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallNative.cpp with 76% similarity]
src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.cs [new file with mode: 0644]
src/tests/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest.csproj [new file with mode: 0644]
src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.cs [new file with mode: 0644]
src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.csproj [new file with mode: 0644]
src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.cs [moved from src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.cs with 97% similarity]
src/tests/JIT/Directed/callconv/ThisCall/ThisCallTest.csproj [moved from src/tests/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest.csproj with 54% similarity]
src/tests/issues.targets

index e510400..a2a7b0e 100644 (file)
@@ -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."
 
index e3b4c90..a4c5e6d 100644 (file)
@@ -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
 
index ae70256..c971d80 100644 (file)
@@ -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
index 005871f..e8250cd 100644 (file)
@@ -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;
     }
index 286e570..f3c1265 100644 (file)
@@ -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++;
index 4142b4d..52d696e 100644 (file)
@@ -564,6 +564,7 @@ namespace Internal.JitInterface
             }
 
             bool found = false;
+            bool memberFunctionVariant = false;
             foreach (CustomAttributeTypedArgument<TypeDesc> 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);
index ea37d07..e787a52 100644 (file)
@@ -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
index a07cd45..71ecf17 100644 (file)
@@ -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)
index 04b87ac..76bccc3 100644 (file)
@@ -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);
         }
index 3791460..38314fa 100644 (file)
@@ -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<size_t S2LEN>
-    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;
index 15c6840..6e28b00 100644 (file)
@@ -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<size_t S2LEN>
+    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).
index 718604f..ab49876 100644 (file)
@@ -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 ...
index 7da6a1b..f829212 100644 (file)
@@ -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;
index 56b0a53..a501994 100644 (file)
@@ -30,8 +30,20 @@ namespace System.Runtime.CompilerServices
         /// </summary>
         public CallConvSuppressGCTransition() { }
     }
+
     public class CallConvThiscall
     {
         public CallConvThiscall() { }
     }
+
+    /// <summary>
+    /// Indicates that the calling convention used is the member function variant.
+    /// </summary>
+    public class CallConvMemberFunction
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="CallConvMemberFunction" /> class.
+        /// </summary>
+        public CallConvMemberFunction() { }
+    }
 }
index a07dee6..ef66619 100644 (file)
@@ -9198,6 +9198,10 @@ namespace System.Runtime.CompilerServices
     {
         public CallConvFastcall() { }
     }
+    public partial class CallConvMemberFunction
+    {
+        public CallConvMemberFunction() { }
+    }
     public partial class CallConvStdcall
     {
         public CallConvStdcall() { }
index 3fc083d..35781ef 100644 (file)
@@ -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
 
index 172214b..a39791f 100644 (file)
@@ -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 (file)
index 305104d..0000000
+++ /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 (file)
index 0000000..97d95eb
--- /dev/null
@@ -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 (file)
index 0000000..746150b
--- /dev/null
@@ -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]<C*, int, SizeF> getSize;
+            public delegate* unmanaged[Cdecl, MemberFunction]<C*, Width> getWidth;
+            public delegate* unmanaged[Cdecl, MemberFunction]<C*, IntWrapper> getHeightAsInt;
+            public delegate* unmanaged[Cdecl, MemberFunction]<C*, E> getE;
+            public delegate* unmanaged[Cdecl, MemberFunction]<C*, CLong> 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 (file)
index 0000000..3d1c4a3
--- /dev/null
@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="*.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="../CMakeLists.txt" />
+    <ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
+  </ItemGroup>
+</Project>
@@ -2,9 +2,12 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 #include <stdio.h>
-#include <xplatform.h>
 #include <platformdefines.h>
 
+#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 (file)
index 0000000..bb1429a
--- /dev/null
@@ -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]<C*, int, SizeF> getSize;
+            public delegate* unmanaged[MemberFunction]<C*, Width> getWidth;
+            public delegate* unmanaged[MemberFunction]<C*, IntWrapper> getHeightAsInt;
+            public delegate* unmanaged[MemberFunction]<C*, E> getE;
+            public delegate* unmanaged[MemberFunction]<C*, CLong> 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 (file)
index 0000000..3d1c4a3
--- /dev/null
@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="*.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="../CMakeLists.txt" />
+    <ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.cs b/src/tests/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest.cs
new file mode 100644 (file)
index 0000000..f3480f0
--- /dev/null
@@ -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]<C*, int, SizeF> getSize;
+            public delegate* unmanaged[Stdcall, MemberFunction]<C*, Width> getWidth;
+            public delegate* unmanaged[Stdcall, MemberFunction]<C*, IntWrapper> getHeightAsInt;
+            public delegate* unmanaged[Stdcall, MemberFunction]<C*, E> getE;
+            public delegate* unmanaged[Stdcall, MemberFunction]<C*, CLong> 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 (file)
index 0000000..3d1c4a3
--- /dev/null
@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="*.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="../CMakeLists.txt" />
+    <ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
+  </ItemGroup>
+</Project>
@@ -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<ThisCallNative.GetSizeFn>(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]<ThisCallNative.C*, ThisCallNative.SizeF>)&GetSize;
+                unmanagedCallersOnlyVtable->getSize = (IntPtr)(delegate* unmanaged[Thiscall]<ThisCallNative.C*, int, ThisCallNative.SizeF>)&GetSize;
                 unmanagedCallersOnlyVtable->getWidth = (IntPtr)(delegate* unmanaged[Thiscall]<ThisCallNative.C*, ThisCallNative.Width>)&GetWidth;
                 unmanagedCallersOnlyVtable->getHeightAsInt = (IntPtr)(delegate* unmanaged[Thiscall]<ThisCallNative.C*, ThisCallNative.IntWrapper>)&GetHeightAsInt;
                 unmanagedCallersOnlyVtable->getE = (IntPtr)(delegate* unmanaged[Thiscall]<ThisCallNative.C*, ThisCallNative.E>)&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
         {
@@ -4,9 +4,10 @@
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
   <ItemGroup>
-    <Compile Include="ThisCallTest.cs" />
+    <Compile Include="*.cs" />
   </ItemGroup>
   <ItemGroup>
-    <ProjectReference Include="CMakeLists.txt" />
+    <ProjectReference Include="../CMakeLists.txt" />
+    <ProjectReference Include="$(TestSourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
   </ItemGroup>
 </Project>
index dede30e..78d51c1 100644 (file)
         <ExcludeList Include="$(XunitTestBinBase)/Interop/IJW/NativeCallingManaged/NativeCallingManaged/*">
             <Issue>https://github.com/dotnet/runtime/issues/12299</Issue>
         </ExcludeList>
-        <ExcludeList Include="$(XUnitTestBinBase)/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest/*">
-            <Issue>Thiscall not supported on Windows ARM32.</Issue>
+        <ExcludeList Include="$(XUnitTestBinBase)/JIT/Directed/callconv/ThisCall/ThisCallTest/*">
+            <Issue>Native member function calling conventions not supported on Windows ARM32.</Issue>
+        </ExcludeList>
+        <ExcludeList Include="$(XUnitTestBinBase)/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest/*">
+            <Issue>Native member function calling conventions not supported on Windows ARM32.</Issue>
+        </ExcludeList>
+        <ExcludeList Include="$(XUnitTestBinBase)/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest/*">
+            <Issue>Native member function calling conventions not supported on Windows ARM32.</Issue>
+        </ExcludeList>
+        <ExcludeList Include="$(XUnitTestBinBase)/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest/*">
+            <Issue>Native member function calling conventions not supported on Windows ARM32.</Issue>
         </ExcludeList>
         <ExcludeList Include="$(XunitTestBinBase)/JIT/Regression/CLR-x86-JIT/V1-M13-RTM/b88793/b88793/*">
             <Issue>https://github.com/dotnet/runtime/issues/12979</Issue>
         <ExcludeList Include = "$(XunitTestBinBase)/Interop/PInvoke/Miscellaneous/MultipleAssembliesWithSamePInvoke/MAWSPITest/**">
             <Issue>https://github.com/dotnet/runtime/issues/41519</Issue>
         </ExcludeList>
-        <ExcludeList Include = "$(XunitTestBinBase)/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest/**">
-            <Issue>https://github.com/dotnet/runtime/issues/41519</Issue>
-        </ExcludeList>
         <ExcludeList Include = "$(XunitTestBinBase)/Interop/PInvoke/Primitives/Pointer/NonBlittablePointer/**">
             <Issue>https://github.com/dotnet/runtime/issues/41519</Issue>
         </ExcludeList>
         <ExcludeList Include = "$(XunitTestBinBase)/JIT/CodeGenBringUpTests/LocallocLarge_r/**">
             <Issue>https://github.com/dotnet/runtime/issues/41472</Issue>
         </ExcludeList>
+        <ExcludeList Include="$(XUnitTestBinBase)/JIT/Directed/callconv/ThisCall/ThisCallTest/*">
+            <Issue>https://github.com/dotnet/runtime/issues/41519</Issue>
+        </ExcludeList>
+        <ExcludeList Include="$(XUnitTestBinBase)/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest/*">
+            <Issue>https://github.com/dotnet/runtime/issues/41519</Issue>
+        </ExcludeList>
+        <ExcludeList Include="$(XUnitTestBinBase)/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest/*">
+            <Issue>https://github.com/dotnet/runtime/issues/41519</Issue>
+        </ExcludeList>
+        <ExcludeList Include="$(XUnitTestBinBase)/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest/*">
+            <Issue>https://github.com/dotnet/runtime/issues/41519</Issue>
+        </ExcludeList>
         <ExcludeList Include = "$(XunitTestBinBase)/Loader/classloader/TypeInitialization/CircularCctors/CircularCctorFourThreadsBFI/**">
             <Issue>https://github.com/dotnet/runtime/issues/41472</Issue>
         </ExcludeList>
         <ExcludeList Include = "$(XunitTestBinBase)/Interop/PInvoke/Miscellaneous/HandleRef/HandleRefTest/**">
             <Issue>needs triage</Issue>
         </ExcludeList>
-        <ExcludeList Include = "$(XunitTestBinBase)/Interop/PInvoke/Miscellaneous/ThisCall/ThisCallTest/**">
-            <Issue>needs triage</Issue>
-        </ExcludeList>
         <ExcludeList Include = "$(XunitTestBinBase)/Interop/UnmanagedCallersOnly/UnmanagedCallersOnlyTest/**">
             <Issue>needs triage</Issue>
         </ExcludeList>
         <ExcludeList Include = "$(XunitTestBinBase)/JIT/CheckProjects/CheckProjects/**">
             <Issue>needs triage</Issue>
         </ExcludeList>
+        <ExcludeList Include="$(XUnitTestBinBase)/JIT/Directed/callconv/ThisCall/ThisCallTest/*">
+            <Issue>needs triage</Issue>
+        </ExcludeList>
+        <ExcludeList Include="$(XUnitTestBinBase)/JIT/Directed/callconv/StdCallMemberFunction/StdCallMemberFunctionTest/*">
+            <Issue>needs triage</Issue>
+        </ExcludeList>
+        <ExcludeList Include="$(XUnitTestBinBase)/JIT/Directed/callconv/PlatformDefaultMemberFunction/PlatformDefaultMemberFunctionTest/*">
+            <Issue>needs triage</Issue>
+        </ExcludeList>
+        <ExcludeList Include="$(XUnitTestBinBase)/JIT/Directed/callconv/CdeclMemberFunction/CdeclMemberFunctionTest/*">
+            <Issue>needs triage</Issue>
+        </ExcludeList>
         <ExcludeList Include = "$(XunitTestBinBase)/JIT/HardwareIntrinsics/Arm/Aes/Aes_r/**">
             <Issue>https://github.com/dotnet/runtime/issues/44648</Issue>
         </ExcludeList>