Repository: dotnet/runtime
Pull request #114495: Add internal intrinsics for instance-based calli
Author: login: steveharter
Labels: name: area-System.Reflection.Emit
Created: 2025-04-10T16:28:11Z | Merged: 2025-04-16T14:14:01Z

Contributes to https://github.com/dotnet/runtime/issues/112994. The next step in this process will use these intrinsics.

Adds support for both CoreClr JIT and R2R to modify the function pointer signature to add HasThis | ExplicitThis so specific function pointer signatures can be called on an instance method.

Local testing verified both:
- R2R\crossgen2 created the native code with the HasThis | ExplicitThis  signature and thus prevented JIT.
- The non-R2R runtime cases replaced the IL of the method and added HasThis | ExplicitThis to the signature.

The tests added here currently don't verify the intrinsics, but an earlier version did (see the first commit). Once the next step of hooking into reflection is done, these tests will cover the new intrinsics added here. In particular, the new tests address missing test cases of calling an instance-based method with non-primitive return types such as `Guid`, `DateTime` and `TimeSpan` that would fail if reflection called these methods with function pointers \ Calli without HasThis | ExplicitThis.


Diff:
diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
index 1317b219771b1e..3d2947219a4780 100644
--- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -170,6 +170,7 @@
     <Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\SignatureHelper.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Reflection\Emit\SymbolMethod.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Reflection\FieldInfo.CoreCLR.cs" />
+    <Compile Include="$(BclSourcesRoot)\System\Reflection\InstanceCalliHelper.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Reflection\LoaderAllocator.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Reflection\MdConstant.cs" />
     <Compile Include="$(BclSourcesRoot)\System\Reflection\MdFieldInfo.cs" />
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Reflection/InstanceCalliHelper.cs b/src/coreclr/System.Private.CoreLib/src/System/Reflection/InstanceCalliHelper.cs
new file mode 100644
index 00000000000000..f79445292e7cbc
--- /dev/null
+++ b/src/coreclr/System.Private.CoreLib/src/System/Reflection/InstanceCalliHelper.cs
@@ -0,0 +1,166 @@
+﻿// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+
+namespace System.Reflection
+{
+    /// <summary>
+    /// Provides a set of helper methods for calling instance methods using calli.
+    /// This is necessary since C# function pointers currently do not support instance methods.
+    /// </summary>
+    internal static unsafe class InstanceCalliHelper
+    {
+        // Zero parameter methods such as property getters:
+
+        [Intrinsic]
+        internal static bool Call(delegate*<object, bool> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static byte Call(delegate*<object, byte> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static char Call(delegate*<object, char> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static DateTime Call(delegate*<object, DateTime> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static DateTimeOffset Call(delegate*<object, DateTimeOffset> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static decimal Call(delegate*<object, decimal> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static double Call(delegate*<object, double> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static float Call(delegate*<object, float> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static Guid Call(delegate*<object, Guid> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static short Call(delegate*<object, short> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static int Call(delegate*<object, int> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static long Call(delegate*<object, long> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static nint Call(delegate*<object, nint> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static nuint Call(delegate*<object, nuint> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static object? Call(delegate*<object, object?> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static sbyte Call(delegate*<object, sbyte> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static ushort Call(delegate*<object, ushort> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static uint Call(delegate*<object, uint> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static ulong Call(delegate*<object, ulong> fn, object o) => fn(o);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, void> fn, object o) => fn(o);
+
+        // One parameter methods with no return such as property setters:
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, bool, void> fn, object o, bool arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, byte, void> fn, object o, byte arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, char, void> fn, object o, char arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, DateTime, void> fn, object o, DateTime arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, DateTimeOffset, void> fn, object o, DateTimeOffset arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, decimal, void> fn, object o, decimal arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, double, void> fn, object o, double arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, float, void> fn, object o, float arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, Guid, void> fn, object o, Guid arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, short, void> fn, object o, short arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, int, void> fn, object o, int arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, long, void> fn, object o, long arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, nint, void> fn, object o, nint arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, nuint, void> fn, object o, nuint arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, object?, void> fn, object o, object? arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, sbyte, void> fn, object o, sbyte arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, ushort, void> fn, object o, ushort arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, uint, void> fn, object o, uint arg1) => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, ulong, void> fn, object o, ulong arg1) => fn(o, arg1);
+
+        // Other methods:
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, object?, object?, void> fn, object o, object? arg1, object? arg2)
+            => fn(o, arg1, arg2);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, object?, object?, object?, void> fn, object o, object? arg1, object? arg2, object? arg3)
+            => fn(o, arg1, arg2, arg3);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, object?, object?, object?, object?, void> fn, object o, object? arg1, object? arg2, object? arg3, object? arg4)
+            => fn(o, arg1, arg2, arg3, arg4);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, object?, object?, object?, object?, object?, void> fn, object o, object? arg1, object? arg2, object? arg3, object? arg4, object? arg5)
+            => fn(o, arg1, arg2, arg3, arg4, arg5);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, object?, object?, object?, object?, object?, object?, void> fn, object o, object? arg1, object? arg2, object? arg3, object? arg4, object? arg5, object? arg6)
+            => fn(o, arg1, arg2, arg3, arg4, arg5, arg6);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, IEnumerable<object>?, void> fn, object o, IEnumerable<object>? arg1)
+            => fn(o, arg1);
+
+        [Intrinsic]
+        internal static void Call(delegate*<object, IEnumerable<object>?, IEnumerable<object>?, void> fn, object o, IEnumerable<object>? arg1, IEnumerable<object>? arg2)
+            => fn(o, arg1, arg2);
+    }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs
index 7fed0872c7dc50..425ba366f1ef19 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs
@@ -85,6 +85,11 @@ private MethodIL TryGetIntrinsicMethodIL(MethodDesc method)
                 return UnsafeIntrinsics.EmitIL(method);
             }
 
+            if (mdType.Name == "InstanceCalliHelper" && mdType.Namespace == "System.Reflection")
+            {
+                return InstanceCalliHelperIntrinsics.EmitIL(method);
+            }
+
             return null;
         }
 
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/InstanceCalliHelperIntrinsics.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/InstanceCalliHelperIntrinsics.cs
new file mode 100644
index 00000000000000..7f188461de7391
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/Stubs/InstanceCalliHelperIntrinsics.cs
@@ -0,0 +1,60 @@
+// 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.Metadata.Ecma335;
+ using Internal.TypeSystem;
+ using Internal.TypeSystem.Ecma;
+
+namespace Internal.IL
+{
+    public class InstanceCalliHelperIntrinsics
+    {
+        public static MethodIL EmitIL(MethodDesc method)
+        {
+            MethodIL methodIL = EcmaMethodIL.Create((EcmaMethod)method);
+
+            if (method.Name.StartsWith("Invoke", StringComparison.Ordinal))
+            {
+                methodIL = new ExplicitThisCall(methodIL);
+            }
+
+            return methodIL;
+        }
+
+        private class ExplicitThisCall : MethodIL
+        {
+            private readonly MethodIL _wrappedMethodIL;
+
+            public ExplicitThisCall(MethodIL wrapped)
+            {
+                _wrappedMethodIL = wrapped;
+            }
+
+            // MethodIL overrides:
+            public override int MaxStack => _wrappedMethodIL.MaxStack;
+            public override bool IsInitLocals => _wrappedMethodIL.IsInitLocals;
+            public override byte[] GetILBytes() => _wrappedMethodIL.GetILBytes();
+            public override LocalVariableDefinition[] GetLocals() => _wrappedMethodIL.GetLocals();
+            public override ILExceptionRegion[] GetExceptionRegions() => _wrappedMethodIL.GetExceptionRegions();
+            public override MethodDebugInformation GetDebugInfo() => _wrappedMethodIL.GetDebugInfo();
+
+            // MethodILScope overrides:
+            public override MethodIL GetMethodILDefinition() => _wrappedMethodIL.GetMethodILDefinition();
+            public override MethodDesc OwningMethod => _wrappedMethodIL.OwningMethod;
+            public override string ToString() => _wrappedMethodIL.ToString();
+            public override object GetObject(int token, NotFoundBehavior notFoundBehavior)
+            {
+                object item = _wrappedMethodIL.GetObject(token, notFoundBehavior);
+                if (item is MethodSignature sig)
+                {
+                    var builder = new MethodSignatureBuilder(sig);
+                    builder.Flags = (sig.Flags | MethodSignatureFlags.ExplicitThis) & ~MethodSignatureFlags.Static;
+                    item = builder.ToSignature();
+                }
+
+                return item;
+            }
+        }
+    }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
index 25b03db8ad8246..043daa1bad8c21 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
@@ -270,6 +270,7 @@
     <Compile Include="Compiler\ReadyToRunXmlRootProvider.cs" />
     <Compile Include="IBC\IBCProfileData.cs" />
     <Compile Include="IBC\IBCProfileParser.cs" />
+    <Compile Include="IL\Stubs\InstanceCalliHelperIntrinsics.cs" />
     <Compile Include="IL\Stubs\PInvokeILEmitter.cs" />
     <Compile Include="Interop\IL\Marshaller.ReadyToRun.cs" />
     <Compile Include="Compiler\PerfEventSource.cs" />
diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt
index 436fc7ae5db107..7fbeffa86df59d 100644
--- a/src/coreclr/vm/CMakeLists.txt
+++ b/src/coreclr/vm/CMakeLists.txt
@@ -340,6 +340,7 @@ set(VM_SOURCES_WKS
     hosting.cpp
     hostinformation.cpp
     ilmarshalers.cpp
+    instancecalli.cpp
     interopconverter.cpp
     interoputil.cpp
     interpexec.cpp
diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h
index 88a1dd9f2e81d3..48404f7c10af57 100644
--- a/src/coreclr/vm/corelib.h
+++ b/src/coreclr/vm/corelib.h
@@ -495,8 +495,10 @@ DEFINE_CLASS(MEMBER,                Reflection,             MemberInfo)
 
 DEFINE_CLASS(METHODBASEINVOKER,     Reflection,             MethodBaseInvoker)
 
-DEFINE_CLASS_U(Reflection,             RuntimeMethodInfo,  NoClass)
-DEFINE_FIELD_U(m_handle,                   ReflectMethodObject, m_pMD)
+DEFINE_CLASS(INSTANCE_CALLI_HELPER, Reflection,             InstanceCalliHelper)
+
+DEFINE_CLASS_U(Reflection,          RuntimeMethodInfo,      NoClass)
+DEFINE_FIELD_U(m_handle,            ReflectMethodObject,    m_pMD)
 DEFINE_CLASS(METHOD,                Reflection,             RuntimeMethodInfo)
 DEFINE_METHOD(METHOD,               INVOKE,                 Invoke,                     IM_Obj_BindingFlags_Binder_ArrObj_CultureInfo_RetObj)
 DEFINE_METHOD(METHOD,               GET_PARAMETERS,         GetParameters,              IM_RetArrParameterInfo)
diff --git a/src/coreclr/vm/instancecalli.cpp b/src/coreclr/vm/instancecalli.cpp
new file mode 100644
index 00000000000000..7e3d7d1604ea93
--- /dev/null
+++ b/src/coreclr/vm/instancecalli.cpp
@@ -0,0 +1,86 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() != 0);
+
+    // Intrinsic must be a static method.
+    _ASSERTE(IsStatic());
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the function pointer and the "this" argument.
+    _ASSERTE(argCount >= 2);
+
+    SigTypeContext genericContext;
+    ILStubLinker sl(
+        this->GetModule(),
+        GetSignature(),
+        &genericContext,
+        this,
+        (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+
+    // Copy the first arg which has the function pointer signature into a SigBuilder to add HASTHIS and EXPLICITTHIS
+    // and to get a token for the new signature.
+    // The signature follows details defined in ECMA-335 - II.23.2.1
+    declarationSig.SkipArg();
+    SigPointer sp = declarationSig.GetArgProps();
+
+    CorElementType eType;
+    sp.GetElemType(&eType); // Skip past the element type to get at the signature.
+    _ASSERTE(eType == ELEMENT_TYPE_FNPTR);
+
+    SigBuilder sigBuilder;
+    sp.CopySignature(GetModule(), &sigBuilder, IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS);
+
+    // Create a token for the new signature.
+    DWORD sigLen;
+    PCCOR_SIGNATURE pSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature((DWORD*)&sigLen);
+    mdToken fcnPtrToken = pCode->GetSigToken(pSig, sigLen);
+
+    // Load "this" and each argument to the function pointer.
+    for (UINT i = 1; i < argCount; ++i)
+        pCode->EmitLDARG(i);
+
+    // Load the function pointer and call it.
+    pCode->EmitLDARG(0);
+    pCode->EmitCALLI(fcnPtrToken, argCount - 1, declarationSig.IsReturnTypeVoid() ? 0 : 1);
+    pCode->EmitRET();
+
+    // Generate all IL associated data for JIT.
+    NewHolder<ILStubResolver> ilResolver = new ILStubResolver();
+    ilResolver->SetStubMethodDesc(this);
+
+    {
+        UINT maxStack;
+        size_t cbCode = sl.Link(&maxStack);
+        DWORD cbSig = sl.GetLocalSigSize();
+
+        COR_ILMETHOD_DECODER* pILHeader = ilResolver->AllocGeneratedIL(cbCode, cbSig, maxStack);
+        BYTE* pbBuffer = (BYTE*)pILHeader->Code;
+        BYTE* pbLocalSig = (BYTE*)pILHeader->LocalVarSig;
+        _ASSERTE(cbSig == pILHeader->cbLocalVarSig);
+        sl.GenerateCode(pbBuffer, cbCode);
+        sl.GetLocalSig(pbLocalSig, cbSig);
+
+        // Store the token lookup map.
+        ilResolver->SetTokenLookupMap(sl.GetTokenLookupMap());
+        ilResolver->SetJitFlags(CORJIT_FLAGS(CORJIT_FLAGS::CORJIT_FLAG_IL_STUB));
+
+        *resolver = (DynamicResolver*)ilResolver;
+        *methodILDecoder = pILHeader;
+    }
+
+    ilResolver.SuppressRelease();
+}
diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp
index 77ffeec4f70ccc..49cd367d99eaa5 100644
--- a/src/coreclr/vm/jitinterface.cpp
+++ b/src/coreclr/vm/jitinterface.cpp
@@ -7555,6 +7555,13 @@ static void getMethodInfoHelper(
             {
                 fILIntrinsic = getILIntrinsicImplementationForActivator(ftn, methInfo, &localSig);
             }
+            else if (CoreLibBinder::IsClass(pMT, CLASS__INSTANCE_CALLI_HELPER))
+            {
+                // We can ignore the existing header, since we are going to generate a new one.
+                cxt.Header = NULL;
+
+                ftn->GenerateFunctionPointerCall(&cxt.TransientResolver, &cxt.Header);
+            }
         }
 
         scopeHnd = cxt.HasTransientMethodDetails()
diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp
index 73db7083022bd9..0a9ff181416760 100644
--- a/src/coreclr/vm/method.hpp
+++ b/src/coreclr/vm/method.hpp
@@ -1886,6 +1886,7 @@ class MethodDesc
     PCODE JitCompileCodeLocked(PrepareCodeConfig* pConfig, COR_ILMETHOD_DECODER* pilHeader, JitListLockEntry* pLockEntry, ULONG* pSizeOfCode);
 
 public:
+    void GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder);
     bool TryGenerateUnsafeAccessor(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder);
 #endif // DACCESS_COMPILE
 
diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp
index fc77ef7cb2aa00..1333e99755eb34 100644
--- a/src/coreclr/vm/siginfo.cpp
+++ b/src/coreclr/vm/siginfo.cpp
@@ -386,6 +386,21 @@ void SigPointer::ConvertToInternalSignature(Module* pSigModule, SigTypeContext *
         cArgs--;
     }
 }
+
+void SigPointer::CopySignature(Module* pSigModule, SigBuilder* pSigBuilder, BYTE additionalCallConv)
+{
+    CONTRACTL
+    {
+        INSTANCE_CHECK;
+        STANDARD_VM_CHECK;
+    }
+    CONTRACTL_END
+
+    SigPointer spEnd(*this);
+    IfFailThrowBF(spEnd.SkipSignature(), BFA_BAD_COMPLUS_SIG, pSigModule);
+    pSigBuilder->AppendByte(*m_ptr | additionalCallConv);
+    pSigBuilder->AppendBlob((const PVOID)(m_ptr + 1), spEnd.m_ptr - (m_ptr + 1));
+}
 #endif // DACCESS_COMPILE
 
 
diff --git a/src/coreclr/vm/siginfo.hpp b/src/coreclr/vm/siginfo.hpp
index 1a425ac942c614..99256bea5072f8 100644
--- a/src/coreclr/vm/siginfo.hpp
+++ b/src/coreclr/vm/siginfo.hpp
@@ -129,7 +129,7 @@ class SigPointer : public SigParser
 
         void ConvertToInternalExactlyOne(Module* pSigModule, SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier = TRUE);
         void ConvertToInternalSignature(Module* pSigModule, SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier = TRUE);
-
+        void CopySignature(Module* pSigModule, SigBuilder * pSigBuilder, BYTE additionalCallConv);
 
     //=========================================================================
     // The CLOSED interface for reading signatures.  With the following
diff --git a/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodCommonTests.cs b/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodCommonTests.cs
index 77d78080e5ea85..21a8227568ed25 100644
--- a/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodCommonTests.cs
+++ b/src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodCommonTests.cs
@@ -1,6 +1,7 @@
 ﻿// Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Collections.Generic;
 using System.Linq;
 using Xunit;
 
@@ -307,5 +308,253 @@ private class TestClassThatThrows
         {
             public static void Throw() => throw new Exception("Here");
         }
+
+        public static IEnumerable<object[]> PropertyTestData()
+        {
+            yield return new object[] { "Bool", TestClass.BoolValue };
+            yield return new object[] { "Byte", TestClass.ByteValue };
+            yield return new object[] { "ByteEnum", TestClass.ByteEnumValue };
+            yield return new object[] { "Char", TestClass.CharValue };
+            yield return new object[] { "DateTimeOffset", TestClass.DateTimeOffsetValue };
+            yield return new object[] { "DateTime", TestClass.DateTimeValue };
+            yield return new object[] { "Decimal", TestClass.DecimalValue };
+            yield return new object[] { "Double", TestClass.DoubleValue };
+            yield return new object[] { "Guid", TestClass.GuidValue };
+            yield return new object[] { "Int16", TestClass.Int16Value };
+            yield return new object[] { "Int16Enum", TestClass.Int16EnumValue };
+            yield return new object[] { "Int32", TestClass.Int32Value };
+            yield return new object[] { "Int32Enum", TestClass.Int32EnumValue };
+            yield return new object[] { "Int64", TestClass.Int64Value };
+            yield return new object[] { "Int64Enum", TestClass.Int64EnumValue };
+            yield return new object[] { "NInt", TestClass.NIntValue };
+            yield return new object[] { "NUInt", TestClass.NUIntValue };
+            yield return new object[] { "Object", TestClass.ObjectValue };
+            yield return new object[] { "SByte", TestClass.SByteValue };
+            yield return new object[] { "SByteEnum", TestClass.SByteEnumValue };
+            yield return new object[] { "Single", TestClass.SingleValue };
+            yield return new object[] { "UInt16", TestClass.UInt16Value };
+            yield return new object[] { "UInt16Enum", TestClass.UInt16EnumValue };
+            yield return new object[] { "UInt32", TestClass.UInt32Value };
+            yield return new object[] { "UInt32Enum", TestClass.UInt32EnumValue };
+            yield return new object[] { "UInt64", TestClass.UInt64Value };
+            yield return new object[] { "UInt64Enum", TestClass.UInt64EnumValue };
+        }
+
+        [Theory]
+        [MemberData(nameof(PropertyTestData))]
+        public void TestProperties(string typeName, object value)
+        {
+            TestClass testClass = new();
+            MethodInfo setter = typeof(TestClass).GetProperty(typeName).GetSetMethod();
+            setter.Invoke(testClass, new object[] { value });
+            MethodInfo getter = typeof(TestClass).GetProperty(typeName).GetGetMethod();
+            object ret = getter.Invoke(testClass, null);
+            Assert.Equal(value, ret);
+        }
+
+        [Fact]
+        public void TestObject2Method()
+        {
+            TestClass testClass = new TestClass();
+            MethodInfo method = typeof(TestClass).GetMethod(nameof(TestClass.Object2));
+            method.Invoke(testClass, new object[] { new object(), new object() });
+            Assert.Equal(nameof(TestClass.Object2), testClass.MethodCalled);
+        }
+
+        [Fact]
+        public void TestObject3Method()
+        {
+            TestClass testClass = new TestClass();
+            MethodInfo method = typeof(TestClass).GetMethod(nameof(TestClass.Object3));
+            method.Invoke(testClass, new object[] { new object(), new object(), new object() });
+            Assert.Equal(nameof(TestClass.Object3), testClass.MethodCalled);
+        }
+
+        [Fact]
+        public void TestObject4Method()
+        {
+            TestClass testClass = new TestClass();
+            MethodInfo method = typeof(TestClass).GetMethod(nameof(TestClass.Object4));
+            method.Invoke(testClass, new object[] { new object(), new object(), new object(), new object() });
+            Assert.Equal(nameof(TestClass.Object4), testClass.MethodCalled);
+        }
+
+        [Fact]
+        public void TestObject5Method()
+        {
+            TestClass testClass = new TestClass();
+            MethodInfo method = typeof(TestClass).GetMethod(nameof(TestClass.Object5));
+            method.Invoke(testClass, new object[] { new object(), new object(), new object(), new object(), new object() });
+            Assert.Equal(nameof(TestClass.Object5), testClass.MethodCalled);
+        }
+
+        [Fact]
+        public void TestObject6Method()
+        {
+            TestClass testClass = new TestClass();
+            MethodInfo method = typeof(TestClass).GetMethod(nameof(TestClass.Object6));
+            method.Invoke(testClass, new object[] { new object(), new object(), new object(), new object(), new object(), new object() });
+            Assert.Equal(nameof(TestClass.Object6), testClass.MethodCalled);
+        }
+
+        [Fact]
+        public void TestVoidMethod()
+        {
+            TestClass testClass = new TestClass();
+            MethodInfo method = typeof(TestClass).GetMethod(nameof(TestClass.Void));
+            method.Invoke(testClass, null);
+            Assert.Equal(nameof(TestClass.Void), testClass.MethodCalled);
+        }
+
+        [Fact]
+        public void TestIEnumerable1Method()
+        {
+            TestClass testClass = new TestClass();
+            MethodInfo method = typeof(TestClass).GetMethod(nameof(TestClass.IEnumerableOfT1));
+            method.Invoke(testClass, new object[] { new List<string>() });
+            Assert.Equal(nameof(TestClass.IEnumerableOfT1), testClass.MethodCalled);
+        }
+
+        [Fact]
+        public void TestIEnumerable2Method()
+        {
+            TestClass testClass = new TestClass();
+            MethodInfo method = typeof(TestClass).GetMethod(nameof(TestClass.IEnumerableOfT2));
+            method.Invoke(testClass, new object[] { new List<string>(), new List<string>() });
+            Assert.Equal(nameof(TestClass.IEnumerableOfT2), testClass.MethodCalled);
+        }
+
+        /// <summary>
+        /// Covers signatures used by reflection invoke intrinsics including primitives, common data types and other common signatures.
+        /// </summary>
+        private class TestClass
+        {
+            public const bool BoolValue = true;
+            public const byte ByteValue = Byte.MaxValue;
+            public const ByteEnumType ByteEnumValue = (ByteEnumType)Byte.MaxValue;
+            public const char CharValue = 'S';
+            public static readonly DateTimeOffset DateTimeOffsetValue = DateTimeOffset.MaxValue;
+            public static readonly DateTime DateTimeValue = DateTime.MaxValue;
+            public const decimal DecimalValue = 42m;
+            public const double DoubleValue = 42d;
+            public static readonly Guid GuidValue = new Guid("18B2A161-48B6-4D6C-AF0E-E618C73C5777");
+            public const short Int16Value = Int16.MaxValue;
+            public const Int16EnumType Int16EnumValue = (Int16EnumType)Int16.MaxValue;
+            public const int Int32Value =  Int32.MaxValue;
+            public const Int32EnumType Int32EnumValue = (Int32EnumType)Int32.MaxValue;
+            public const long Int64Value = Int64.MaxValue;
+            public const Int64EnumType Int64EnumValue = (Int64EnumType)Int64.MaxValue;
+            public const nint NIntValue = 42;
+            public const nuint NUIntValue = 42;
+            public static readonly object ObjectValue = new object();
+            public const sbyte SByteValue = SByte.MaxValue;
+            public const SByteEnumType SByteEnumValue = (SByteEnumType)SByte.MaxValue;
+            public const float SingleValue = 42f;
+            public const ushort UInt16Value = UInt16.MaxValue;
+            public const UInt16EnumType UInt16EnumValue = (UInt16EnumType)UInt16.MaxValue;
+            public const uint UInt32Value = UInt32.MaxValue;
+            public const UInt32EnumType UInt32EnumValue = (UInt32EnumType)UInt32.MaxValue;
+            public const ulong UInt64Value = UInt64.MaxValue;
+            public const UInt64EnumType UInt64EnumValue = (UInt64EnumType)UInt64.MaxValue;
+
+            public string MethodCalled;
+
+            public bool Bool { get; set; }
+            public byte Byte { get; set; }
+            public ByteEnumType ByteEnum { get; set; }
+            public char Char { get; set; }
+            public DateTime DateTime { get; set; }
+            public DateTimeOffset DateTimeOffset { get; set; }
+            public decimal Decimal { get; set; }
+            public double Double { get; set; }
+            public float Single { get; set; }
+            public Guid Guid { get; set; }
+            public short Int16 { get; set; }
+            public Int16EnumType Int16Enum { get; set; }
+            public int Int32 { get; set; }
+            public Int32EnumType Int32Enum { get; set; }
+            public long Int64 { get; set; }
+            public Int64EnumType Int64Enum { get; set; }
+            public nint NInt { get; set; }
+            public nuint NUInt { get; set; }
+            public object Object { get; set; }
+            public sbyte SByte { get; set; }
+            public SByteEnumType SByteEnum { get; set; }
+            public ushort UInt16 { get; set; }
+            public UInt16EnumType UInt16Enum { get; set; }
+            public uint UInt32 { get; set; }
+            public UInt32EnumType UInt32Enum { get; set; }
+            public ulong UInt64 { get; set; }
+            public UInt64EnumType UInt64Enum { get; set; }
+
+            public void Void() { MethodCalled = "Void"; }
+
+            public void Object2(object arg1, object arg2)
+            {
+                Assert.NotNull(arg1);
+                Assert.NotNull(arg2);
+                MethodCalled = "Object2";
+            }
+
+            public void Object3(object arg1, object arg2, object arg3)
+            {
+                Assert.NotNull(arg1);
+                Assert.NotNull(arg2);
+                Assert.NotNull(arg3);
+                MethodCalled = "Object3";
+            }
+
+            public void Object4(object arg1, object arg2, object arg3, object arg4)
+            {
+                Assert.NotNull(arg1);
+                Assert.NotNull(arg2);
+                Assert.NotNull(arg3);
+                Assert.NotNull(arg4);
+                MethodCalled = "Object4";
+            }
+
+            public void Object5(object arg1, object arg2, object arg3, object arg4, object arg5)
+            {
+                Assert.NotNull(arg1);
+                Assert.NotNull(arg2);
+                Assert.NotNull(arg3);
+                Assert.NotNull(arg4);
+                Assert.NotNull(arg5);
+                MethodCalled = "Object5";
+            }
+
+            public void Object6(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6)
+            {
+                Assert.NotNull(arg1);
+                Assert.NotNull(arg2);
+                Assert.NotNull(arg3);
+                Assert.NotNull(arg4);
+                Assert.NotNull(arg5);
+                Assert.NotNull(arg6);
+                MethodCalled = "Object6";
+            }
+
+            public void IEnumerableOfT1(IEnumerable<object> arg1)
+            {
+                Assert.NotNull(arg1);
+                MethodCalled = "IEnumerableOfT1";
+            }
+
+            public void IEnumerableOfT2(IEnumerable<object> arg1, IEnumerable<object> arg2)
+            {
+                Assert.NotNull(arg1);
+                Assert.NotNull(arg2);
+                MethodCalled = "IEnumerableOfT2";
+            }
+
+            public enum ByteEnumType : byte { }
+            public enum SByteEnumType : sbyte { }
+            public enum Int16EnumType : short { }
+            public enum Int32EnumType : int { }
+            public enum Int64EnumType : long { }
+            public enum UInt16EnumType : ushort { }
+            public enum UInt32EnumType : uint { }
+            public enum UInt64EnumType : ulong { }
+        }
     }
 }


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: 

Copilot reviewed 12 out of 14 changed files in this pull request and generated 3 comments.

<details>
<summary>Files not reviewed (2)</summary>

* **src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj**: Language not supported
* **src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj**: Language not supported
</details>



state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T16:29:46Z
user: login: steveharter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T21:43:50Z
user: login: jkotas
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T01:15:59Z
user: login: jkotas
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T01:16:23Z
user: login: jkotas
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T01:16:49Z
user: login: jkotas
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T01:17:21Z
user: login: steveharter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T15:12:40Z
user: login: jkotas
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T17:11:39Z
user: login: jkotas
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T17:15:02Z
user: login: jkotas
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T17:17:20Z
user: login: jkotas
body: LGMT otherwise. Thank you!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-11T17:17:43Z
user: login: jkotas
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T17:30:50Z
user: login: jkotas
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T17:33:51Z
user: login: steveharter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T20:53:27Z
user: login: steveharter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T20:53:38Z
user: login: steveharter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T20:54:00Z
user: login: steveharter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T20:57:11Z
user: login: steveharter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T20:57:39Z
user: login: steveharter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T21:00:19Z
user: login: AaronRobinsonMSFT
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T21:45:14Z
user: login: jkotas
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T22:39:02Z
user: login: jkotas
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T23:01:58Z
user: login: steveharter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T17:23:27Z
user: login: steveharter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T19:36:54Z
user: login: steveharter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T19:40:52Z
user: login: jkotas
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-15T07:27:51Z
user: login: steveharter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T15:02:04Z
user: login: jkotas
body: Thanks!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-15T15:05:05Z
user: login: steveharter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T15:08:03Z
user: login: AaronRobinsonMSFT
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-15T17:45:24Z
user: login: steveharter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T18:47:47Z
user: login: steveharter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T18:51:38Z
user: login: AaronRobinsonMSFT
body: Thanks!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-15T18:55:27Z

Review comments:
diff_hunk: @@ -858,7 +858,7 @@ class ILCodeStream
     void EmitBRFALSE    (ILCodeLabel* pCodeLabel);
     void EmitBRTRUE     (ILCodeLabel* pCodeLabel);
     void EmitCALL       (int token, int numInArgs, int numRetArgs);
-    void EmitCALLI      (int token, int numInArgs, int numRetArgs);
+    void EmitCALLI      (int token, int numInArgs, int numRetArgs, bool explictThis = false);
path: src/coreclr/vm/stubgen.h
user: login: Copilot
body: [nitpick] Consider renaming the parameter 'explictThis' to 'explicitThis' to improve clarity and correctness.
```suggestion
    void EmitCALLI      (int token, int numInArgs, int numRetArgs, bool explicitThis = false);
```
created_at: 2025-04-10T16:29:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1212,10 +1212,10 @@ void ILCodeStream::EmitCALLVIRT(int token, int numInArgs, int numRetArgs)
     WRAPPER_NO_CONTRACT;
     Emit(CEE_CALLVIRT, (INT16)(numRetArgs - numInArgs), token);
 }
-void ILCodeStream::EmitCALLI(int token, int numInArgs, int numRetArgs)
+void ILCodeStream::EmitCALLI(int token, int numInArgs, int numRetArgs, bool explictThis)
 {
     WRAPPER_NO_CONTRACT;
-    Emit(CEE_CALLI, (INT16)(numRetArgs - numInArgs - 1), token);
+    Emit(CEE_CALLI, (INT16)(numRetArgs - numInArgs - explictThis ? 0 : 1), token);
path: src/coreclr/vm/stubgen.cpp
user: login: Copilot
body: The subtraction expression uses the ternary operator without proper parentheses, causing unexpected evaluation. It likely should be: Emit(CEE_CALLI, (INT16)(numRetArgs - numInArgs - (explictThis ? 0 : 1)), token);
```suggestion
    Emit(CEE_CALLI, (INT16)(numRetArgs - numInArgs - (explictThis ? 0 : 1)), token);
```
created_at: 2025-04-10T16:29:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -307,5 +308,245 @@ private class TestClassThatThrows
         {
             public static void Throw() => throw new Exception("Here");
         }
+
+        public static IEnumerable<object[]> PropertyTestData()
+        {
+            yield return new object[] { "Bool", TestClass.BoolValue };
+            yield return new object[] { "Byte", TestClass.ByteValue };
+            yield return new object[] { "ByteEnum", TestClass.ByteValue };
path: src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodCommonTests.cs
user: login: Copilot
body: The test case for 'ByteEnum' uses TestClass.ByteValue instead of a value of type ByteEnumType. Consider providing an appropriate ByteEnum value to accurately test the property.
```suggestion
            yield return new object[] { "ByteEnum", ByteEnumType.Value1 };
```
created_at: 2025-04-10T16:29:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -307,5 +308,245 @@ private class TestClassThatThrows
         {
             public static void Throw() => throw new Exception("Here");
         }
+
+        public static IEnumerable<object[]> PropertyTestData()
+        {
+            yield return new object[] { "Bool", TestClass.BoolValue };
+            yield return new object[] { "Byte", TestClass.ByteValue };
+            yield return new object[] { "ByteEnum", TestClass.ByteValue };
path: src/libraries/System.Runtime/tests/System.Reflection.Tests/MethodCommonTests.cs
user: login: steveharter
body: By design since we are testing with underlying type max range
created_at: 2025-04-10T21:43:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,102 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() == 0);
+
+    // Intrinsic must be a static method.
+    if (!IsStatic())
+        ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_UNSAFEACCESSOR);
path: src/coreclr/vm/instancecalli.cpp
user: login: jkotas
body: ```suggestion
    _ASSERTE(IsStatic());
```
These should not use `BFA_INVALID_UNSAFEACCESSOR` error message.

It is fine to just use asserts to check assumptions about intrinsics. Intriniscs are not user defined, so we do not need to protect against bad input.
created_at: 2025-04-11T01:15:59Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,102 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() == 0);
+
+    // Intrinsic must be a static method.
+    if (!IsStatic())
+        ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_UNSAFEACCESSOR);
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
+    if (argCount < 2)
+        ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_UNSAFEACCESSOR);
path: src/coreclr/vm/instancecalli.cpp
user: login: jkotas
body: ```suggestion
    _ASSERTE(argCount >= 2);
```
created_at: 2025-04-11T01:16:23Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,102 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() == 0);
+
+    // Intrinsic must be a static method.
+    if (!IsStatic())
+        ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_UNSAFEACCESSOR);
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
+    if (argCount < 2)
+        ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_UNSAFEACCESSOR);
+
+    SigTypeContext genericContext;
+    ILStubLinker sl(
+        this->GetModule(),
+        GetSignature(),
+        &genericContext,
+        this,
+        (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+
+    // Move to the function pointer argument.
+    declarationSig.SkipArg(); // Skip "this".
+
+    CorElementType fnType = declarationSig.NextArg();
+    if (fnType != ELEMENT_TYPE_FNPTR)
+    {
+        ThrowHR(COR_E_BADIMAGEFORMAT, BFA_INVALID_UNSAFEACCESSOR);
+    }
path: src/coreclr/vm/instancecalli.cpp
user: login: jkotas
body: ```suggestion
    _ASSERTE(fnType == ELEMENT_TYPE_FNPTR);
```
created_at: 2025-04-11T01:16:49Z
author_association: MEMBER
diff_hunk: @@ -495,8 +495,10 @@ DEFINE_CLASS(MEMBER,                Reflection,             MemberInfo)
 
 DEFINE_CLASS(METHODBASEINVOKER,     Reflection,             MethodBaseInvoker)
 
-DEFINE_CLASS_U(Reflection,             RuntimeMethodInfo,  NoClass)
-DEFINE_FIELD_U(m_handle,                   ReflectMethodObject, m_pMD)
+DEFINE_CLASS(INVOKE_HELPERS,        Reflection,             InstanceCalliHelper)
path: src/coreclr/vm/corelib.h
user: login: jkotas
body: ```suggestion
DEFINE_CLASS(INSTANCE_CALLI_HELPER,        Reflection,             InstanceCalliHelper)
```
created_at: 2025-04-11T01:17:20Z
author_association: MEMBER
diff_hunk: @@ -495,8 +495,10 @@ DEFINE_CLASS(MEMBER,                Reflection,             MemberInfo)
 
 DEFINE_CLASS(METHODBASEINVOKER,     Reflection,             MethodBaseInvoker)
 
-DEFINE_CLASS_U(Reflection,             RuntimeMethodInfo,  NoClass)
-DEFINE_FIELD_U(m_handle,                   ReflectMethodObject, m_pMD)
+DEFINE_CLASS(INVOKE_HELPERS,        Reflection,             InstanceCalliHelper)
path: src/coreclr/vm/corelib.h
user: login: steveharter
body: Yikes missed that rename. Thanks
created_at: 2025-04-11T15:12:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() == 0);
+
+    // Intrinsic must be a static method.
+    _ASSERTE(IsStatic());
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
+    _ASSERTE(argCount >= 2);
+
+    SigTypeContext genericContext;
+    ILStubLinker sl(
+        this->GetModule(),
+        GetSignature(),
+        &genericContext,
+        this,
+        (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+
+    // Move to the function pointer argument.
+    declarationSig.SkipArg(); // Skip "this".
+
+    CorElementType fnType = declarationSig.NextArg();
+    _ASSERTE(fnType == ELEMENT_TYPE_FNPTR);
+
+    SigPointer spToken = declarationSig.GetArgProps();
+    spToken.GetByte(NULL); 
+    declarationSig.SkipArg();
+    SigPointer sigPtrTokenEnd = declarationSig.GetArgProps();
+
+    // Copy the existing signature so we can add HAS_THIS and EXPLICIT_THIS.
+    SigBuilder sigBuilder;
+    spToken.CopySignature(GetModule(), &sigBuilder, &sigPtrTokenEnd, IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS);
+
+    // Create a token for the new signature.
+    uint32_t sigLen;
+    PCCOR_SIGNATURE pSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature((DWORD*)&sigLen);
+    mdToken fcnPtrToken = pCode->GetSigToken(pSig, sigLen);
+
+    // Create a local and store the function pointer value in it.
path: src/coreclr/vm/instancecalli.cpp
user: login: jkotas
body: It should not be necessary to store the function pointer in a local. The actual function pointer types do not matter in IL. It is all just pointers.
created_at: 2025-04-11T17:11:38Z
author_association: MEMBER
diff_hunk: @@ -1212,10 +1212,10 @@ void ILCodeStream::EmitCALLVIRT(int token, int numInArgs, int numRetArgs)
     WRAPPER_NO_CONTRACT;
     Emit(CEE_CALLVIRT, (INT16)(numRetArgs - numInArgs), token);
 }
-void ILCodeStream::EmitCALLI(int token, int numInArgs, int numRetArgs)
+void ILCodeStream::EmitCALLI(int token, int numInArgs, int numRetArgs, bool explicitThis)
path: src/coreclr/vm/stubgen.cpp
user: login: jkotas
body: I think it would make more sense to adjust `numInArgs` on the caller side and leave this method as is. The adjustment for regular non-explicit `this` has to be done by caller too.  
created_at: 2025-04-11T17:15:02Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,97 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() == 0);
+
+    // Intrinsic must be a static method.
+    _ASSERTE(IsStatic());
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
+    _ASSERTE(argCount >= 2);
+
+    SigTypeContext genericContext;
+    ILStubLinker sl(
+        this->GetModule(),
+        GetSignature(),
+        &genericContext,
+        this,
+        (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+
+    // Move to the function pointer argument.
+    declarationSig.SkipArg(); // Skip "this".
+
+    CorElementType fnType = declarationSig.NextArg();
+    _ASSERTE(fnType == ELEMENT_TYPE_FNPTR);
+
+    SigPointer spToken = declarationSig.GetArgProps();
+    spToken.GetByte(NULL); 
+    declarationSig.SkipArg();
+    SigPointer sigPtrTokenEnd = declarationSig.GetArgProps();
+
+    // Copy the existing signature so we can add HAS_THIS and EXPLICIT_THIS.
+    SigBuilder sigBuilder;
+    spToken.CopySignature(GetModule(), &sigBuilder, &sigPtrTokenEnd, IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS);
+
+    // Create a token for the new signature.
+    uint32_t sigLen;
+    PCCOR_SIGNATURE pSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature((DWORD*)&sigLen);
+    mdToken fcnPtrToken = pCode->GetSigToken(pSig, sigLen);
+
+    // Create a local and store the function pointer value in it.
+    LocalDesc localDesc(ELEMENT_TYPE_FNPTR);
+    localDesc.pSigModule = GetModule();
+    localDesc.pSig = pSig;
+    DWORD localIndex = pCode->NewLocal(localDesc);
+    pCode->EmitLDARG(1);
+    pCode->EmitSTLOC(localIndex);
+
+    // Load "this" and each argument to the function pointer.
+    pCode->EmitLDARG(0);
+    for (UINT i = 2; i < argCount; ++i)
path: src/coreclr/vm/instancecalli.cpp
user: login: jkotas
body: ```suggestion
    // Load "this" and each argument to the function pointer.
    for (UINT i = 1; i < argCount; ++i)
```
I think it would look better to have the function pointer first, and the this argument and the rest of the arguments after it. RawCalli signatures are like that as well.

created_at: 2025-04-11T17:17:19Z
author_association: MEMBER
diff_hunk: @@ -386,6 +386,26 @@ void SigPointer::ConvertToInternalSignature(Module* pSigModule, SigTypeContext *
         cArgs--;
     }
 }
+
+void SigPointer::CopySignature(Module* pSigModule, SigBuilder* pSigBuilder, SigPointer* pSigPtrTokenEnd, BYTE additionalCallConv)
+{
+    CONTRACTL
+    {
+        INSTANCE_CHECK;
+        STANDARD_VM_CHECK;
+    }
+    CONTRACTL_END
+
+    BYTE element;
+    IfFailThrowBF(GetByte(&element), BFA_BAD_COMPLUS_SIG, pSigModule);
+    pSigBuilder->AppendByte(element | additionalCallConv);
+
+    while (m_ptr != pSigPtrTokenEnd->m_ptr)
+    {
+        IfFailThrowBF(GetByte(&element), BFA_BAD_COMPLUS_SIG, pSigModule);
+        pSigBuilder->AppendByte(element);
+    }
path: src/coreclr/vm/siginfo.cpp
user: login: jkotas
body: ```suggestion
    pSigBuilder->AppendBlob(m_ptr, pSigPtrTokenEnd->m_ptr - m_ptr);
```
Once you do this, it is not clear whether it is worth it to have this as a method on SigPointer. It is very specialized small method. I think the 4 lines can be inline in the one place where this is used.
created_at: 2025-04-11T17:30:50Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,97 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() == 0);
+
+    // Intrinsic must be a static method.
+    _ASSERTE(IsStatic());
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
+    _ASSERTE(argCount >= 2);
+
+    SigTypeContext genericContext;
+    ILStubLinker sl(
+        this->GetModule(),
+        GetSignature(),
+        &genericContext,
+        this,
+        (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+
+    // Move to the function pointer argument.
+    declarationSig.SkipArg(); // Skip "this".
+
+    CorElementType fnType = declarationSig.NextArg();
+    _ASSERTE(fnType == ELEMENT_TYPE_FNPTR);
+
+    SigPointer spToken = declarationSig.GetArgProps();
+    spToken.GetByte(NULL); 
path: src/coreclr/vm/instancecalli.cpp
user: login: jkotas
body: What is this `GetByte` skipping?
created_at: 2025-04-11T17:33:51Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,97 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() == 0);
+
+    // Intrinsic must be a static method.
+    _ASSERTE(IsStatic());
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
+    _ASSERTE(argCount >= 2);
+
+    SigTypeContext genericContext;
+    ILStubLinker sl(
+        this->GetModule(),
+        GetSignature(),
+        &genericContext,
+        this,
+        (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+
+    // Move to the function pointer argument.
+    declarationSig.SkipArg(); // Skip "this".
+
+    CorElementType fnType = declarationSig.NextArg();
+    _ASSERTE(fnType == ELEMENT_TYPE_FNPTR);
+
+    SigPointer spToken = declarationSig.GetArgProps();
+    spToken.GetByte(NULL); 
+    declarationSig.SkipArg();
+    SigPointer sigPtrTokenEnd = declarationSig.GetArgProps();
+
+    // Copy the existing signature so we can add HAS_THIS and EXPLICIT_THIS.
+    SigBuilder sigBuilder;
+    spToken.CopySignature(GetModule(), &sigBuilder, &sigPtrTokenEnd, IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS);
+
+    // Create a token for the new signature.
+    uint32_t sigLen;
+    PCCOR_SIGNATURE pSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature((DWORD*)&sigLen);
+    mdToken fcnPtrToken = pCode->GetSigToken(pSig, sigLen);
+
+    // Create a local and store the function pointer value in it.
path: src/coreclr/vm/instancecalli.cpp
user: login: steveharter
body: Sure - removed (I was following the C# IL for release)
created_at: 2025-04-11T20:53:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1212,10 +1212,10 @@ void ILCodeStream::EmitCALLVIRT(int token, int numInArgs, int numRetArgs)
     WRAPPER_NO_CONTRACT;
     Emit(CEE_CALLVIRT, (INT16)(numRetArgs - numInArgs), token);
 }
-void ILCodeStream::EmitCALLI(int token, int numInArgs, int numRetArgs)
+void ILCodeStream::EmitCALLI(int token, int numInArgs, int numRetArgs, bool explicitThis)
path: src/coreclr/vm/stubgen.cpp
user: login: steveharter
body: Done
created_at: 2025-04-11T20:53:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() == 0);
+
+    // Intrinsic must be a static method.
+    _ASSERTE(IsStatic());
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
+    _ASSERTE(argCount >= 2);
+
+    SigTypeContext genericContext;
+    ILStubLinker sl(
+        this->GetModule(),
+        GetSignature(),
+        &genericContext,
+        this,
+        (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+
+    // Move to the function pointer argument.
+    declarationSig.SkipArg(); // Skip "this".
+
+    CorElementType fnType = declarationSig.NextArg();
+    _ASSERTE(fnType == ELEMENT_TYPE_FNPTR);
+
+    SigPointer spToken = declarationSig.GetArgProps();
+    spToken.GetByte(NULL); 
+    declarationSig.SkipArg();
+    SigPointer sigPtrTokenEnd = declarationSig.GetArgProps();
+
+    // Copy the existing signature so we can add HAS_THIS and EXPLICIT_THIS.
+    SigBuilder sigBuilder;
+    spToken.CopySignature(GetModule(), &sigBuilder, &sigPtrTokenEnd, IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS);
+
+    // Create a token for the new signature.
+    uint32_t sigLen;
+    PCCOR_SIGNATURE pSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature((DWORD*)&sigLen);
+    mdToken fcnPtrToken = pCode->GetSigToken(pSig, sigLen);
+
+    // Create a local and store the function pointer value in it.
+    LocalDesc localDesc(ELEMENT_TYPE_FNPTR);
+    localDesc.pSigModule = GetModule();
+    localDesc.pSig = pSig;
+    DWORD localIndex = pCode->NewLocal(localDesc);
+    pCode->EmitLDARG(1);
+    pCode->EmitSTLOC(localIndex);
+
+    // Load "this" and each argument to the function pointer.
+    pCode->EmitLDARG(0);
+    for (UINT i = 2; i < argCount; ++i)
path: src/coreclr/vm/instancecalli.cpp
user: login: steveharter
body: I went around on this, but yeah looping over is a bit easier. Done.
created_at: 2025-04-11T20:54:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -386,6 +386,26 @@ void SigPointer::ConvertToInternalSignature(Module* pSigModule, SigTypeContext *
         cArgs--;
     }
 }
+
+void SigPointer::CopySignature(Module* pSigModule, SigBuilder* pSigBuilder, SigPointer* pSigPtrTokenEnd, BYTE additionalCallConv)
+{
+    CONTRACTL
+    {
+        INSTANCE_CHECK;
+        STANDARD_VM_CHECK;
+    }
+    CONTRACTL_END
+
+    BYTE element;
+    IfFailThrowBF(GetByte(&element), BFA_BAD_COMPLUS_SIG, pSigModule);
+    pSigBuilder->AppendByte(element | additionalCallConv);
+
+    while (m_ptr != pSigPtrTokenEnd->m_ptr)
+    {
+        IfFailThrowBF(GetByte(&element), BFA_BAD_COMPLUS_SIG, pSigModule);
+        pSigBuilder->AppendByte(element);
+    }
path: src/coreclr/vm/siginfo.cpp
user: login: steveharter
body: I did the main suggestion of AppendBlob, but keeping it as a method prevents having to use discouraged `SigParser::GetPtr()` to get at the protected `m_ptr`
created_at: 2025-04-11T20:57:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() == 0);
+
+    // Intrinsic must be a static method.
+    _ASSERTE(IsStatic());
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
+    _ASSERTE(argCount >= 2);
+
+    SigTypeContext genericContext;
+    ILStubLinker sl(
+        this->GetModule(),
+        GetSignature(),
+        &genericContext,
+        this,
+        (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+
+    // Move to the function pointer argument.
+    declarationSig.SkipArg(); // Skip "this".
+
+    CorElementType fnType = declarationSig.NextArg();
+    _ASSERTE(fnType == ELEMENT_TYPE_FNPTR);
+
+    SigPointer spToken = declarationSig.GetArgProps();
+    spToken.GetByte(NULL); 
path: src/coreclr/vm/instancecalli.cpp
user: login: steveharter
body: I added a comment. The element type (ELEMENT_TYPE_FNPTR) which is just before the signature.
created_at: 2025-04-11T20:57:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder != NULL);
path: src/coreclr/vm/instancecalli.cpp
user: login: steveharter
body: @AaronRobinsonMSFT can you take a look at these Asserts; these were copied from the unsafe accessor work but since there is IL in these methods `*methodILDecoder != NULL` and `GetRVA() != 0` I changed them to reflect that. Not sure if we need to worry about freeing any memory such as the existing `methodILDecoder`.
created_at: 2025-04-11T21:00:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -7555,6 +7555,10 @@ static void getMethodInfoHelper(
             {
                 fILIntrinsic = getILIntrinsicImplementationForActivator(ftn, methInfo, &localSig);
             }
+            else if (CoreLibBinder::IsClass(ftn->GetMethodTable(), CLASS__INSTANCE_CALLI_HELPER))
path: src/coreclr/vm/jitinterface.cpp
user: login: AaronRobinsonMSFT
body: ```suggestion
            else if (CoreLibBinder::IsClass(pMT, CLASS__INSTANCE_CALLI_HELPER))
```
created_at: 2025-04-11T21:32:46Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder != NULL);
path: src/coreclr/vm/instancecalli.cpp
user: login: AaronRobinsonMSFT
body: We don't need to free anything here, it is all allocated on the stack. I would keep this assert as expecting `NULL` and set the `Header` field on `MethodInfoHelperContext` in the caller to `NULL`. Since this is an **identified** intrinsic it can be ignored. Adding a comment prior to calling this API to that effect would be appreciated.
created_at: 2025-04-11T21:45:09Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder != NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() != 0);
+
+    // Intrinsic must be a static method.
+    _ASSERTE(IsStatic());
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
+    _ASSERTE(argCount >= 2);
+
+    SigTypeContext genericContext;
+    ILStubLinker sl(
+        this->GetModule(),
+        GetSignature(),
+        &genericContext,
+        this,
+        (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+
+    // Copy the existing signature to add HASTHIS and EXPLICITTHIS.
+    CorElementType fnType = declarationSig.NextArg();
+    _ASSERTE(fnType == ELEMENT_TYPE_FNPTR);
+    SigPointer spToken = declarationSig.GetArgProps();
+    spToken.GetByte(nullptr); // Skip the element type (ELEMENT_TYPE_FNPTR)
+    declarationSig.SkipArg();
+    SigPointer sigPtrTokenEnd = declarationSig.GetArgProps();
+
+    SigBuilder sigBuilder;
+    spToken.CopySignature(GetModule(), &sigBuilder, &sigPtrTokenEnd, IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS);
+
+    // Create a token for the new signature.
+    uint32_t sigLen;
+    PCCOR_SIGNATURE pSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature((DWORD*)&sigLen);
+    mdToken fcnPtrToken = pCode->GetSigToken(pSig, sigLen);
+
+    // Load "this" and each argument to the function pointer.
+    for (UINT i = 1; i < argCount; ++i)
+        pCode->EmitLDARG(i);
+
+    // Load the function pointer and call it.
+    pCode->EmitLDARG(0);
+    pCode->EmitCALLI(fcnPtrToken, argCount - 2, declarationSig.IsReturnTypeVoid() ? 0 : 1);
path: src/coreclr/vm/instancecalli.cpp
user: login: jkotas
body: ```suggestion
    pCode->EmitCALLI(fcnPtrToken, argCount - 1, declarationSig.IsReturnTypeVoid() ? 0 : 1);
```
I think this should be argCount - 1, ie "this" and each argument to the function pointer.
created_at: 2025-04-11T22:39:02Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder != NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() != 0);
+
+    // Intrinsic must be a static method.
+    _ASSERTE(IsStatic());
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
+    _ASSERTE(argCount >= 2);
+
+    SigTypeContext genericContext;
+    ILStubLinker sl(
+        this->GetModule(),
+        GetSignature(),
+        &genericContext,
+        this,
+        (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+
+    // Copy the existing signature to add HASTHIS and EXPLICITTHIS.
+    CorElementType fnType = declarationSig.NextArg();
+    _ASSERTE(fnType == ELEMENT_TYPE_FNPTR);
+    SigPointer spToken = declarationSig.GetArgProps();
+    spToken.GetByte(nullptr); // Skip the element type (ELEMENT_TYPE_FNPTR)
+    declarationSig.SkipArg();
+    SigPointer sigPtrTokenEnd = declarationSig.GetArgProps();
+
+    SigBuilder sigBuilder;
+    spToken.CopySignature(GetModule(), &sigBuilder, &sigPtrTokenEnd, IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS);
path: src/coreclr/vm/instancecalli.cpp
user: login: jkotas
body: ```suggestion
    SigBuilder sigBuilder;
    spToken.CopySignature(GetModule(), &sigBuilder, IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS);
```
created_at: 2025-04-11T22:48:58Z
author_association: MEMBER
diff_hunk: @@ -129,7 +129,7 @@ class SigPointer : public SigParser
 
         void ConvertToInternalExactlyOne(Module* pSigModule, SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier = TRUE);
         void ConvertToInternalSignature(Module* pSigModule, SigTypeContext *pTypeContext, SigBuilder * pSigBuilder, BOOL bSkipCustomModifier = TRUE);
-
+        void CopySignature(Module* pSigModule, SigBuilder * pSigBuilder, SigPointer* pSigPtrTokenEnd, BYTE additionalCallConv);
path: src/coreclr/vm/siginfo.hpp
user: login: jkotas
body: ```suggestion
        void CopySignature(Module* pSigModule, SigBuilder * pSigBuilder, BYTE additionalCallConv);
```
created_at: 2025-04-11T22:49:19Z
author_association: MEMBER
diff_hunk: @@ -386,6 +386,21 @@ void SigPointer::ConvertToInternalSignature(Module* pSigModule, SigTypeContext *
         cArgs--;
     }
 }
+
+void SigPointer::CopySignature(Module* pSigModule, SigBuilder* pSigBuilder, SigPointer* pSigPtrTokenEnd, BYTE additionalCallConv)
path: src/coreclr/vm/siginfo.cpp
user: login: jkotas
body: ```suggestion
void SigPointer::CopySignature(Module* pSigModule, SigBuilder* pSigBuilder, BYTE additionalCallConv)
```
created_at: 2025-04-11T22:49:43Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder != NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() != 0);
+
+    // Intrinsic must be a static method.
+    _ASSERTE(IsStatic());
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
+    _ASSERTE(argCount >= 2);
+
+    SigTypeContext genericContext;
+    ILStubLinker sl(
+        this->GetModule(),
+        GetSignature(),
+        &genericContext,
+        this,
+        (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+
+    // Copy the existing signature to add HASTHIS and EXPLICITTHIS.
+    CorElementType fnType = declarationSig.NextArg();
+    _ASSERTE(fnType == ELEMENT_TYPE_FNPTR);
+    SigPointer spToken = declarationSig.GetArgProps();
+    spToken.GetByte(nullptr); // Skip the element type (ELEMENT_TYPE_FNPTR)
+    declarationSig.SkipArg();
+    SigPointer sigPtrTokenEnd = declarationSig.GetArgProps();
+
+    SigBuilder sigBuilder;
+    spToken.CopySignature(GetModule(), &sigBuilder, &sigPtrTokenEnd, IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS);
+
+    // Create a token for the new signature.
+    uint32_t sigLen;
+    PCCOR_SIGNATURE pSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature((DWORD*)&sigLen);
path: src/coreclr/vm/instancecalli.cpp
user: login: jkotas
body: ```suggestion
    DWORD sigLen;
    PCCOR_SIGNATURE pSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature(&sigLen);
```
Avoid cast
created_at: 2025-04-11T22:55:12Z
author_association: MEMBER
diff_hunk: @@ -386,6 +386,21 @@ void SigPointer::ConvertToInternalSignature(Module* pSigModule, SigTypeContext *
         cArgs--;
     }
 }
+
+void SigPointer::CopySignature(Module* pSigModule, SigBuilder* pSigBuilder, SigPointer* pSigPtrTokenEnd, BYTE additionalCallConv)
+{
+    CONTRACTL
+    {
+        INSTANCE_CHECK;
+        STANDARD_VM_CHECK;
+    }
+    CONTRACTL_END
+
+    BYTE element;
+    IfFailThrowBF(GetByte(&element), BFA_BAD_COMPLUS_SIG, pSigModule);
+    pSigBuilder->AppendByte(element | additionalCallConv);
+    pSigBuilder->AppendBlob((const PVOID)m_ptr, pSigPtrTokenEnd->m_ptr - m_ptr);
path: src/coreclr/vm/siginfo.cpp
user: login: jkotas
body: ```suggestion
    SigPointer spEnd(*this);
    IfFailThrowBF(spEnd.SkipSignature(), BFA_BAD_COMPLUS_SIG, pSigModule);

    pSigBuilder->AppendByte(*m_ptr | additionalCallConv);
    pSigBuilder->AppendBlob((const PVOID)m_ptr, spEnd.m_ptr - (m_ptr + 1));
```
If you would like to keep this as separate method, I would move the logic that computes the length of the signature into this method too, so that it is more self-contained. Also, it can be simplified a bit more by just reading the byte from `*m_ptr` and not modifying where `this` instance points to.
created_at: 2025-04-11T23:01:55Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder != NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() != 0);
+
+    // Intrinsic must be a static method.
+    _ASSERTE(IsStatic());
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
+    _ASSERTE(argCount >= 2);
+
+    SigTypeContext genericContext;
+    ILStubLinker sl(
+        this->GetModule(),
+        GetSignature(),
+        &genericContext,
+        this,
+        (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+
+    // Copy the existing signature to add HASTHIS and EXPLICITTHIS.
+    CorElementType fnType = declarationSig.NextArg();
+    _ASSERTE(fnType == ELEMENT_TYPE_FNPTR);
+    SigPointer spToken = declarationSig.GetArgProps();
+    spToken.GetByte(nullptr); // Skip the element type (ELEMENT_TYPE_FNPTR)
+    declarationSig.SkipArg();
+    SigPointer sigPtrTokenEnd = declarationSig.GetArgProps();
+
+    SigBuilder sigBuilder;
+    spToken.CopySignature(GetModule(), &sigBuilder, &sigPtrTokenEnd, IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS);
+
+    // Create a token for the new signature.
+    uint32_t sigLen;
+    PCCOR_SIGNATURE pSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature((DWORD*)&sigLen);
+    mdToken fcnPtrToken = pCode->GetSigToken(pSig, sigLen);
+
+    // Load "this" and each argument to the function pointer.
+    for (UINT i = 1; i < argCount; ++i)
+        pCode->EmitLDARG(i);
+
+    // Load the function pointer and call it.
+    pCode->EmitLDARG(0);
+    pCode->EmitCALLI(fcnPtrToken, argCount - 2, declarationSig.IsReturnTypeVoid() ? 0 : 1);
path: src/coreclr/vm/instancecalli.cpp
user: login: steveharter
body: Yes it looks like we need to adjust by just 1 due to EmitCallI() doing that extra -1 since it is likely assuming the signature is ImplicitThis and thus argCount doesn't include "this".

https://github.com/dotnet/runtime/blob/d6cd5777ad238107d66503fb834abbaab849f440/src/coreclr/vm/stubgen.cpp#L1218

Examples:

For a property getter we have incoming
1(numRetArgs) + 2 (argCount)
but want to end up with a -1 for stack delta (pop 2, push 1):
1(numRetArgs) - 1 (numInArgs) - 1

For a property setter we have incoming
0(numRetArgs) + 3 (argCount)
but want to end up with a -3 (pop 3)
0(numRetArgs) - 2 (numInArgs) - 1
created_at: 2025-04-14T17:23:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -7555,6 +7555,10 @@ static void getMethodInfoHelper(
             {
                 fILIntrinsic = getILIntrinsicImplementationForActivator(ftn, methInfo, &localSig);
             }
+            else if (CoreLibBinder::IsClass(ftn->GetMethodTable(), CLASS__INSTANCE_CALLI_HELPER))
path: src/coreclr/vm/jitinterface.cpp
user: login: steveharter
body: Thanks
created_at: 2025-04-14T19:36:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -386,6 +386,21 @@ void SigPointer::ConvertToInternalSignature(Module* pSigModule, SigTypeContext *
         cArgs--;
     }
 }
+
+void SigPointer::CopySignature(Module* pSigModule, SigBuilder* pSigBuilder, SigPointer* pSigPtrTokenEnd, BYTE additionalCallConv)
+{
+    CONTRACTL
+    {
+        INSTANCE_CHECK;
+        STANDARD_VM_CHECK;
+    }
+    CONTRACTL_END
+
+    BYTE element;
+    IfFailThrowBF(GetByte(&element), BFA_BAD_COMPLUS_SIG, pSigModule);
+    pSigBuilder->AppendByte(element | additionalCallConv);
+    pSigBuilder->AppendBlob((const PVOID)m_ptr, pSigPtrTokenEnd->m_ptr - m_ptr);
path: src/coreclr/vm/siginfo.cpp
user: login: steveharter
body: Keeping as a separate method avoids leaking the protected ptr and length fields.

I removed the incoming "end" sigpointer as shown above. Also since "GetByte()" is no longer called, m_ptr must have + 1 added.

Also, for the method to no have unexpected side-effects, it either needs to advance m_ptr all the way to the end or just leave it as-is at the beginning. I just left it as-is and did the +1 and -1 for the calling convention byte.
created_at: 2025-04-14T19:40:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -386,6 +386,20 @@ void SigPointer::ConvertToInternalSignature(Module* pSigModule, SigTypeContext *
         cArgs--;
     }
 }
+
+void SigPointer::CopySignature(Module* pSigModule, SigBuilder* pSigBuilder, BYTE additionalCallConv)
+{
+    CONTRACTL
+    {
+        INSTANCE_CHECK;
+        STANDARD_VM_CHECK;
+    }
+    CONTRACTL_END
+
+    // The copy is done without advancing m_ptr.
+    pSigBuilder->AppendByte(*m_ptr | additionalCallConv);
+    pSigBuilder->AppendBlob((const PVOID)(m_ptr + 1), m_dwLen - 1);
path: src/coreclr/vm/siginfo.cpp
user: login: jkotas
body: This is going to copy everything in the SigPointer, not just the signature. It does not seem like a good idea. I am not sure about where it may cause things to go wrong.

I think you should only copy what's required. https://github.com/dotnet/runtime/pull/114495/#discussion_r2040391681 was my suggestion for how to do it.
created_at: 2025-04-15T07:27:51Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder != NULL);
path: src/coreclr/vm/instancecalli.cpp
user: login: steveharter
body: Thanks
created_at: 2025-04-15T15:02:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -386,6 +386,20 @@ void SigPointer::ConvertToInternalSignature(Module* pSigModule, SigTypeContext *
         cArgs--;
     }
 }
+
+void SigPointer::CopySignature(Module* pSigModule, SigBuilder* pSigBuilder, BYTE additionalCallConv)
+{
+    CONTRACTL
+    {
+        INSTANCE_CHECK;
+        STANDARD_VM_CHECK;
+    }
+    CONTRACTL_END
+
+    // The copy is done without advancing m_ptr.
+    pSigBuilder->AppendByte(*m_ptr | additionalCallConv);
+    pSigBuilder->AppendBlob((const PVOID)(m_ptr + 1), m_dwLen - 1);
path: src/coreclr/vm/siginfo.cpp
user: login: steveharter
body: Yep sorry regressed here. I was trying to avoid a second skip\parse of the signature; the current position of the next unprocessed is there in MetaSig but it doesn't expose it (it only exposes the last start of argument) probably to help detect invalid sigs or usage. Also updated the calling code a bit to make it more obvious when we skip the element type.
created_at: 2025-04-15T15:08:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() != 0);
+
+    // Intrinsic must be a static method.
+    _ASSERTE(IsStatic());
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
+    _ASSERTE(argCount >= 2);
+
+    SigTypeContext genericContext;
+    ILStubLinker sl(
+        this->GetModule(),
+        GetSignature(),
+        &genericContext,
+        this,
+        (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+
+    // Copy the first arg which has the function pointer signature in order to add HASTHIS and EXPLICITTHIS.
path: src/coreclr/vm/instancecalli.cpp
user: login: AaronRobinsonMSFT
body: This comment states we are copying the first arg, but right below we call `SkipArg()`. That is a bit confusing. Perhaps reference the signature format from ECMA-335 we are following here. I do this a few times in the `UnsafeAccessor` logic so people know where to reference the format being assumed.
created_at: 2025-04-15T17:34:42Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() != 0);
+
+    // Intrinsic must be a static method.
+    _ASSERTE(IsStatic());
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
path: src/coreclr/vm/instancecalli.cpp
user: login: AaronRobinsonMSFT
body: Nit: This description implies the order is `void M(object o, void* fptr, ...)`. I think the truth is function pointer and then "this" pointer.
created_at: 2025-04-15T17:39:31Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() != 0);
+
+    // Intrinsic must be a static method.
+    _ASSERTE(IsStatic());
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
path: src/coreclr/vm/instancecalli.cpp
user: login: steveharter
body: Thanks, re-worded this.
created_at: 2025-04-15T18:47:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "common.h"
+
+void MethodDesc::GenerateFunctionPointerCall(DynamicResolver** resolver, COR_ILMETHOD_DECODER** methodILDecoder)
+{
+    STANDARD_VM_CONTRACT;
+    _ASSERTE(resolver != NULL);
+    _ASSERTE(methodILDecoder != NULL);
+    _ASSERTE(*resolver == NULL && *methodILDecoder == NULL);
+    _ASSERTE(IsIL());
+    _ASSERTE(GetRVA() != 0);
+
+    // Intrinsic must be a static method.
+    _ASSERTE(IsStatic());
+
+    MetaSig declarationSig(this);
+    UINT argCount = declarationSig.NumFixedArgs();
+
+    // Intrinsic must have at least the "this" argument and the IntPtr function pointer argument.
+    _ASSERTE(argCount >= 2);
+
+    SigTypeContext genericContext;
+    ILStubLinker sl(
+        this->GetModule(),
+        GetSignature(),
+        &genericContext,
+        this,
+        (ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);
+
+    ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
+
+    // Copy the first arg which has the function pointer signature in order to add HASTHIS and EXPLICITTHIS.
path: src/coreclr/vm/instancecalli.cpp
user: login: steveharter
body: The nature of those APIs are you have to do a SkipArg() + GetArgProps() to get a pointer to the first arg. GetArgProps() should be called "GetLastArg()" or something like that. Public access to the current parsing pointer is not provided.

I added the ECMA-335 link although we are treating the actual signature opaque in this method; just leaving it up to "skip()" semantics to get the start and end pointers so we can do a binary copy.
created_at: 2025-04-15T18:51:38Z
author_association: CONTRIBUTOR

PR comments:
user: login: dotnet-policy-service[bot]
created_at: 2025-04-10T16:28:58Z
body: Tagging subscribers to this area: @dotnet/area-system-reflection-emit
See info in [area-owners.md](https://github.com/dotnet/runtime/blob/main/docs/area-owners.md) if you want to be subscribed.
<!-- Policy app identification https://img.shields.io/static/v1?label=PullRequestIssueManagement. -->
author_association: CONTRIBUTOR
user: login: MichalPetryka
created_at: 2025-04-10T23:39:42Z
body: Wouldn't it be simpler to just write those stubs in IL instead of having to implement them in every VM?
Like [this](https://sharplab.io/#v2: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).
author_association: CONTRIBUTOR
user: login: jkotas
created_at: 2025-04-11T01:19:08Z
body: > Wouldn't it be simpler to just write those stubs in IL instead of having to implement them in every VM?

We would need to invent build infrastructure to merge this IL into CoreLib....
author_association: MEMBER
user: login: steveharter
created_at: 2025-04-16T14:13:32Z
body: /ba-g infra timeout on building Libraries Test Run release coreclr linux_musl x64 Debug
author_association: CONTRIBUTOR