Open mjsabby opened 5 years ago
The flag to check if an EEType
(CoreRT) contains pointers is 0x0020
. The flag to check if a MethodTable
(CoreCLR) has pointers is 0x0100
. This is annoying because now I have to have a forked code path for CoreRT.
I wonder why this was done ...
CoreRT Code Path:
while (objectId < buffer + length)
{
var typeHandle = runtimeTypeHandles[(int)Marshal.ReadIntPtr((IntPtr)objectId)].Value;
var eetype = (EEType*)typeHandle;
var objectSize = eetype->BaseSize;
var numComponents = Marshal.ReadInt32((IntPtr)objectId, IntPtr.Size);
objectSize += numComponents * eetype->ComponentSize;
bool containsPointers = (eetype->Flags & 0x0020) == 0x0020;
if (containsPointers)
{
var entries = *(int*)((byte*)eetype - IntPtr.Size);
if (entries < 0)
{
entries -= entries;
}
var slots = 1 + entries * 2;
var gcdesc = new GCDesc(buffer, (byte*)eetype - (slots * IntPtr.Size), slots * IntPtr.Size);
if (IntPtr.Size == 8)
{
gcdesc.FixupObject64(objectId, objectSize);
}
else
{
gcdesc.FixupObject32(objectId, objectSize);
}
}
Marshal.WriteIntPtr((IntPtr)objectId, (IntPtr)eetype);
objectId += objectSize + Padding(objectSize, IntPtr.Size);
}
CoreCLR code path:
while (objectId < buffer + length)
{
var typeHandle = runtimeTypeHandles[(int)Marshal.ReadIntPtr((IntPtr)objectId)].Value;
bool isArray = (typeHandle.ToInt64() & 0x2) == 0x2;
var mt = (MethodTable*)typeHandle;
if (isArray)
{
mt = (MethodTable*)Marshal.ReadIntPtr(typeHandle, 6);
}
var objectSize = mt->BaseSize;
var flags = mt->Flags;
bool hasComponentSize = (flags & 0x80000000) == 0x80000000;
if (hasComponentSize)
{
var numComponents = Marshal.ReadInt32((IntPtr)objectId, IntPtr.Size);
objectSize += numComponents * mt->ComponentSize;
}
bool containsPointerOrCollectible = (flags & 0x10000000) == 0x10000000 || (flags & 0x1000000) == 0x1000000;
if (containsPointerOrCollectible)
{
var entries = *(int*)((byte*)mt - IntPtr.Size);
if (entries < 0)
{
entries -= entries;
}
var slots = 1 + entries * 2;
var gcdesc = new GCDesc(buffer, (byte*)mt - (slots * IntPtr.Size), slots * IntPtr.Size);
if (IntPtr.Size == 8)
{
gcdesc.FixupObject64(objectId, objectSize);
}
else
{
gcdesc.FixupObject32(objectId, objectSize);
}
}
Marshal.WriteIntPtr((IntPtr)objectId, (IntPtr)mt);
objectId += objectSize + Padding(objectSize, IntPtr.Size);
}
Notice that in the CoreCLR path I have to first check if it is an array, and switch from TypeDesc to MT. Then the containsPointerOrCollectible
check is different.
Unrelated note: I'm also checking hasComponentSize
, which I don't think I need to since in places in CoreCLR I've seen a blind read of numComponents
even if it is not an array or string because it gets multiplied by componentSize
. I guess the question is, the check is helping or not?
After bringing this up with @MichalStrehovsky & @jkotas I will fork the code paths for CoreRT as we don't want to modify the EEType bits to conform to the CoreCLR MT.
Part of the work is exposing the helpers from S.P.C so our serialized can call it. https://github.com/dotnet/corert/pull/7589
The other part is actually understanding how to go from an
EEType*
toGCDesc
when deserializing. And for serializing hoping and praying that the layout algorithm is the same so that the serializer can run on .NET Core and deserialize on CoreRT when doing it on the same OS and architecture.