diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index a26e71dab0fad4..ac41bcefaff092 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -815,6 +815,8 @@ static PCODE CreateILDelegateShuffleThunk(MethodDesc* pDelegateMD, bool callTarg pCode->EmitLoadThis(); pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__METHOD_PTR_AUX))); + // TODO: For open virtual delegates, _methodPtrAux is a dispatch stub that expects the address of _methodPtrAux in a + // hidden argument, which calli does not pass: https://github.com/dotnet/runtime/issues/134733 pCode->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, sig.NumFixedArgs(), sig.IsReturnTypeVoid() ? 0 : 1); pCode->EmitRET(); diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 70d5928cb5820e..0e21a28c23b16d 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -1358,6 +1358,21 @@ static void ShiftDelegateCallArgs(int8_t* stack, int32_t callArgsOffset, int32_t } } +// Resolves the target of an open virtual delegate for the given 'this' argument. +static MethodDesc* ResolveOpenVirtualDelegateTarget(DELEGATEREF delegateObj, OBJECTREF* pThisArg) +{ + MethodDesc* pDeclMD = COMDelegate::GetMethodDescForOpenVirtualDelegate(delegateObj); + return CallWithSEHWrapper( + [pDeclMD, pThisArg]() { + MethodTable* pMT = (*pThisArg)->GetMethodTable(); + MethodDesc* pTarget; + GCX_PREEMP_REGION_BEGIN(); + pTarget = pDeclMD->GetMethodDescOfVirtualizedCode(pThisArg, pMT, pDeclMD->GetMethodTable()); + GCX_PREEMP_REGION_END(); + return pTarget; + }); +} + static void UpdateFrameForTailCall(InterpMethodContextFrame *pFrame, PTR_InterpByteCodeStart targetIp, int8_t *callArgsAddress) { InterpMethod *pTargetMethod = targetIp->Method; @@ -3376,6 +3391,24 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr InvokeUnmanagedCalliWithTransition(calliFunctionPointer, cookie, stack, pFrame, callArgsAddress, returnValueAddress); } } +#ifdef FEATURE_CACHED_INTERFACE_DISPATCH + else if (calliFunctionPointer == (PCODE)CID_VirtualOpenDelegateDispatch) + { + // For an open virtual delegate, _methodPtrAux is CID_VirtualOpenDelegateDispatch, + // which expects the address of _methodPtrAux in a hidden argument that calli cannot + // express; resolve the target as INTOP_CALLDELEGATE does. + // Workaround for https://github.com/dotnet/runtime/issues/134733. + + // The shuffle thunk's 'this' is the delegate. + DELEGATEREF delegateObj = LOCAL_VAR(0, DELEGATEREF); + _ASSERTE(((MethodDesc*)pMethod->methodHnd)->IsILStub() && ((MethodDesc*)pMethod->methodHnd)->AsDynamicMethodDesc()->IsDelegateShuffleThunk()); + _ASSERTE(delegateObj != NULL && delegateObj->GetMethodPtrAux() == calliFunctionPointer); + OBJECTREF *pThisArg = (OBJECTREF*)callArgsAddress; + NULL_CHECK(*pThisArg); + targetMethod = ResolveOpenVirtualDelegateTarget(delegateObj, pThisArg); + goto CALL_INTERP_METHOD; + } +#endif // FEATURE_CACHED_INTERFACE_DISPATCH #ifndef FEATURE_PORTABLE_ENTRYPOINTS // If we're not using portable entrypoints, we can use NonVirtualEntry2MethodDesc to figure out where tailcalls go. Since this is // somewhat expensive, we only do it for tailcalls which are relatively rare. @@ -3500,18 +3533,9 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr if (isOpenVirtual) { - targetMethod = COMDelegate::GetMethodDescForOpenVirtualDelegate(*delegateObj); OBJECTREF *pThisArg = LOCAL_VAR_ADDR(callArgsOffset + INTERP_STACK_SLOT_SIZE, OBJECTREF); NULL_CHECK(*pThisArg); - targetMethod = CallWithSEHWrapper( - [&targetMethod, &pThisArg]() { - MethodTable* pMT = (*pThisArg)->GetMethodTable(); - MethodDesc *pTarget; - GCX_PREEMP_REGION_BEGIN(); - pTarget = targetMethod->GetMethodDescOfVirtualizedCode(pThisArg, pMT, targetMethod->GetMethodTable()); - GCX_PREEMP_REGION_END(); - return pTarget; - }); + targetMethod = ResolveOpenVirtualDelegateTarget(*delegateObj, pThisArg); } else { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DelegateTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DelegateTests.cs index e145ac41227eac..4275fd0f8becaa 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DelegateTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DelegateTests.cs @@ -553,6 +553,24 @@ public static void DifferentOpenVirtualDelegates() Assert.Equal(m2, b.Method); } + [Fact] + public static void OpenVirtualDelegates_InvokeResolvesOverride() + { + Func toString = typeof(object).GetMethod(nameof(object.ToString)).CreateDelegate>(); + Assert.Equal(nameof(OpenVirtualDerived), toString(new OpenVirtualDerived())); + Assert.Equal(typeof(Struct).ToString(), toString(new Struct())); + Assert.Equal(nameof(DayOfWeek.Monday), toString(DayOfWeek.Monday)); + } + + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/134707", typeof(PlatformDetection), nameof(PlatformDetection.IsBrowser), nameof(PlatformDetection.IsMonoAOT))] + public static void OpenVirtualDelegates_InterfaceMethod_InvokeResolvesImplementation() + { + Func interfaceMethod = typeof(IOpenVirtual).GetMethod(nameof(IOpenVirtual.M)).CreateDelegate>(); + Assert.Equal(1, interfaceMethod(new OpenVirtualDerived())); + Assert.Equal(2, interfaceMethod(new OpenVirtualStruct())); + } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsTypeEquivalenceSupported))] public static void TypeEquivalentDelegatesPointingToSameMethod_AreEqualAndHaveSameHashCode() { @@ -588,6 +606,14 @@ internal virtual void M1() { } internal virtual void M2() { } } + interface IOpenVirtual { int M(); } + class OpenVirtualDerived : IOpenVirtual + { + public int M() => 1; + public override string ToString() => nameof(OpenVirtualDerived); + } + struct OpenVirtualStruct : IOpenVirtual { public int M() => 2; } + class Base { public virtual void M() { } } class Derived : Base { public override void M() { } } diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index af586522303251..7bac0a358e42ca 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -90,8 +90,6 @@ - -