microsoft / FrozenObjects

FrozenObjects serializer and deserializer
MIT License
90 stars 14 forks source link

CoreRT support #4

Open mjsabby opened 5 years ago

mjsabby commented 5 years ago

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* to GCDesc 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.

mjsabby commented 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 ...

mjsabby commented 5 years ago

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?

mjsabby commented 5 years ago

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.