Issue #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.
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;
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
}
}
+ [MethodImplAttribute(MethodImplOptions.NoInlining)]
+ static void TestGenericNonVirtualMethod()
+ {
+ var c = new MyChildGeneric<string>();
+ Assert.AreEqual(CallGeneric(c), "MyGeneric.NonVirtualMethod");
+ }
+
+ [MethodImplAttribute(MethodImplOptions.NoInlining)]
+ static string CallGeneric<T>(MyGeneric<T, T> g)
+ {
+ return g.NonVirtualMethod();
+ }
+
static void TestInstanceFields()
{
var t = new InstanceFieldTest2();
TestGenericVirtualMethod();
TestMovedGenericVirtualMethod();
+ TestGenericNonVirtualMethod();
TestInstanceFields();
return typeof(List<W>).ToString();
}
#endif
+
+ public string NonVirtualMethod()
+ {
+ return "MyGeneric.NonVirtualMethod";
+ }
}
public class MyChildGeneric<T> : MyGeneric<T,T>