From 8de59413d0a388ac178ed7c425fab375a7070fdb Mon Sep 17 00:00:00 2001 From: JohnChen0 Date: Thu, 26 May 2016 15:07:00 -0700 Subject: [PATCH] Fix generic non-virtual method call in Ready-to-Run images Issue dotnet/coreclr#5201 revealed a bug in the Ready-to-Run implementation. This bug can cause the runtime to dispatch a method call to the wrong target when all the following conditions are met: * A shared generic method in a Ready-to-Run module calls a method in a shared generic class. * The target is a non-virtual instance method, but is called through callvirt instruction (as C# compiler normally does). * The target is in a different module. * The target method is defined in a base class, while the actual object instance is of a derived class. This commit fixes this bug by changing a virtual call to a regular call when the target is non-virtual. Commit migrated from https://github.com/dotnet/coreclr/commit/4fd217d564b743f995426de7bd642166e0a84849 --- src/coreclr/src/vm/genericdict.cpp | 8 ++++++++ src/coreclr/src/vm/jitinterface.cpp | 2 +- src/coreclr/tests/src/readytorun/main.cs | 14 ++++++++++++++ src/coreclr/tests/src/readytorun/test.cs | 5 +++++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/coreclr/src/vm/genericdict.cpp b/src/coreclr/src/vm/genericdict.cpp index 87eef87..04e2550 100644 --- a/src/coreclr/src/vm/genericdict.cpp +++ b/src/coreclr/src/vm/genericdict.cpp @@ -880,6 +880,14 @@ Dictionary::PopulateEntry( result = (CORINFO_GENERIC_HANDLE)pMethod->GetMultiCallableAddrOfCode(); } else + if (kind == DispatchStubAddrSlot) + { + _ASSERTE((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) == 0); + PCODE *ppCode = (PCODE*)(void*)pMethod->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(PCODE))); + *ppCode = pMethod->GetMultiCallableAddrOfCode(); + result = (CORINFO_GENERIC_HANDLE)ppCode; + } + else { _ASSERTE(kind == MethodDescSlot); result = (CORINFO_GENERIC_HANDLE)pMethod; diff --git a/src/coreclr/src/vm/jitinterface.cpp b/src/coreclr/src/vm/jitinterface.cpp index 5a120bd..3bdb245 100644 --- a/src/coreclr/src/vm/jitinterface.cpp +++ b/src/coreclr/src/vm/jitinterface.cpp @@ -3468,7 +3468,7 @@ NoSpecialCase: methodFlags |= ENCODE_METHOD_SIG_SlotInsteadOfToken; } else - if (entryKind == DispatchStubAddrSlot) + if (entryKind == DispatchStubAddrSlot && pTemplateMD->IsVtableMethod()) { // Encode the method for dispatch stub using slot to avoid touching the interface method MethodDesc at runtime diff --git a/src/coreclr/tests/src/readytorun/main.cs b/src/coreclr/tests/src/readytorun/main.cs index 70157bc..9b947fd 100644 --- a/src/coreclr/tests/src/readytorun/main.cs +++ b/src/coreclr/tests/src/readytorun/main.cs @@ -203,6 +203,19 @@ class Program } } + [MethodImplAttribute(MethodImplOptions.NoInlining)] + static void TestGenericNonVirtualMethod() + { + var c = new MyChildGeneric(); + Assert.AreEqual(CallGeneric(c), "MyGeneric.NonVirtualMethod"); + } + + [MethodImplAttribute(MethodImplOptions.NoInlining)] + static string CallGeneric(MyGeneric g) + { + return g.NonVirtualMethod(); + } + static void TestInstanceFields() { var t = new InstanceFieldTest2(); @@ -375,6 +388,7 @@ class Program TestGenericVirtualMethod(); TestMovedGenericVirtualMethod(); + TestGenericNonVirtualMethod(); TestInstanceFields(); diff --git a/src/coreclr/tests/src/readytorun/test.cs b/src/coreclr/tests/src/readytorun/test.cs index 7e14398..39afbd2 100644 --- a/src/coreclr/tests/src/readytorun/test.cs +++ b/src/coreclr/tests/src/readytorun/test.cs @@ -255,6 +255,11 @@ public class MyGeneric return typeof(List).ToString(); } #endif + + public string NonVirtualMethod() + { + return "MyGeneric.NonVirtualMethod"; + } } public class MyChildGeneric : MyGeneric -- 2.7.4