static void EmitDeserializeUnionCase(
            ILGenerator il,
            Type type,
            UnionSerializationInfo info,
            LocalBuilder unionKey,
            FieldBuilder stringByteKeysField,
            ArgumentField reader,
            ArgumentField argOptions,
            LocalBuilder localResolver
            )
        {
            // options.Security.DepthStep(ref reader);
            argOptions.EmitLoad();
            il.EmitCall(getSecurityFromOptions);
            reader.EmitLdarg();
            il.EmitCall(securityDepthStep);

            // var length = ReadMapHeader(ref byteSequence);
            LocalBuilder length = il.DeclareLocal(typeof(int)); // [loc:1]

            reader.EmitLdarg();

            if (info.IsIntKey)
            {
                il.EmitCall(MessagePackReaderTypeInfo.ReadArrayHeader);
            }
            else
            {
                il.EmitCall(MessagePackReaderTypeInfo.ReadMapHeader);
            }

            il.EmitStloc(length);

            // make local fields
            Label?gotoDefault = null;

            DeserializeInfo[] infoList;
            if (info.IsIntKey)
            {
                var maxKey    = info.Members.Select(x => x.IntKey).DefaultIfEmpty(-1).Max();
                var len       = maxKey + 1;
                var intKeyMap = info.Members.ToDictionary(x => x.IntKey);

                infoList = Enumerable.Range(0, len)
                           .Select(x =>
                {
                    UnionSerializationInfo.EmittableMember member;
                    if (intKeyMap.TryGetValue(x, out member))
                    {
                        return(new DeserializeInfo
                        {
                            MemberInfo = member,
                            LocalField = il.DeclareLocal(member.Type),
                            SwitchLabel = il.DefineLabel()
                        });
                    }
                    else
                    {
                        // return null MemberInfo, should filter null
                        if (gotoDefault == null)
                        {
                            gotoDefault = il.DefineLabel();
                        }
                        return(new DeserializeInfo
                        {
                            MemberInfo = null,
                            LocalField = null,
                            SwitchLabel = gotoDefault.Value,
                        });
                    }
                })
                           .ToArray();
            }
            else
            {
                infoList = info.Members
                           .Select(item => new DeserializeInfo
                {
                    MemberInfo  = item,
                    LocalField  = il.DeclareLocal(item.Type),
                    SwitchLabel = il.DefineLabel()
                })
                           .ToArray();
            }

            // Read Loop(for var i = 0; i< length; i++)
            if (info.IsStringKey)
            {
                var automata = new AutomataDictionary();
                for (int i = 0; i < info.Members.Length; i++)
                {
                    automata.Add(info.Members[i].StringKey, i);
                }

                LocalBuilder buffer  = il.DeclareLocal(typeof(ReadOnlySpan <byte>));
                LocalBuilder longKey = il.DeclareLocal(typeof(ulong));

                // for (int i = 0; i < len; i++)
                il.EmitIncrementFor(length, forILocal =>
                {
                    Label readNext = il.DefineLabel();
                    Label loopEnd  = il.DefineLabel();

                    reader.EmitLdarg();
                    il.EmitCall(ReadStringSpan);
                    il.EmitStloc(buffer);

                    // gen automata name lookup
                    automata.EmitMatch(
                        il,
                        buffer,
                        longKey,
                        x =>
                    {
                        var i = x.Value;
                        if (infoList[i].MemberInfo != null)
                        {
                            EmitDeserializeValue(il, infoList[i], i, reader, argOptions, localResolver);
                            il.Emit(OpCodes.Br, loopEnd);
                        }
                        else
                        {
                            il.Emit(OpCodes.Br, readNext);
                        }
                    },
                        () =>
                    {
                        il.Emit(OpCodes.Br, readNext);
                    });

                    il.MarkLabel(readNext);
                    reader.EmitLdarg();
                    il.EmitCall(MessagePackReaderTypeInfo.Skip);

                    il.MarkLabel(loopEnd);
                });
            }
            else
            {
                LocalBuilder key           = il.DeclareLocal(typeof(int));
                Label        switchDefault = il.DefineLabel();

                il.EmitIncrementFor(length, forILocal =>
                {
                    Label loopEnd = il.DefineLabel();

                    il.EmitLdloc(forILocal);
                    il.EmitStloc(key);

                    // switch... local = Deserialize
                    il.EmitLdloc(key);

                    il.Emit(OpCodes.Switch, infoList.Select(x => x.SwitchLabel).ToArray());

                    il.MarkLabel(switchDefault);

                    // default, only read. reader.ReadNextBlock();
                    reader.EmitLdarg();
                    il.EmitCall(MessagePackReaderTypeInfo.Skip);
                    il.Emit(OpCodes.Br, loopEnd);

                    if (gotoDefault != null)
                    {
                        il.MarkLabel(gotoDefault.Value);
                        il.Emit(OpCodes.Br, switchDefault);
                    }

                    var i = 0;
                    foreach (DeserializeInfo item in infoList)
                    {
                        if (item.MemberInfo != null)
                        {
                            il.MarkLabel(item.SwitchLabel);
                            EmitDeserializeValue(il, item, i++, reader, argOptions, localResolver);
                            il.Emit(OpCodes.Br, loopEnd);
                        }
                    }

                    il.MarkLabel(loopEnd);
                });
            }

            // create result union case
            LocalBuilder structLocal = EmitNewObject(il, type, info, infoList);

            // IMessagePackSerializationCallbackReceiver.OnAfterDeserialize()
            if (type.GetTypeInfo().ImplementedInterfaces.Any(x => x == typeof(IMessagePackSerializationCallbackReceiver)))
            {
                // call directly
                var runtimeMethods = type.GetRuntimeMethods().Where(x => x.Name == "OnAfterDeserialize").ToArray();
                if (runtimeMethods.Length == 1)
                {
                    if (info.IsClass)
                    {
                        il.Emit(OpCodes.Dup);
                    }
                    else
                    {
                        il.EmitLdloca(structLocal);
                    }

                    il.Emit(OpCodes.Call, runtimeMethods[0]); // don't use EmitCall helper(must use 'Call')
                }
                else
                {
                    if (info.IsStruct)
                    {
                        il.EmitLdloc(structLocal);
                        il.Emit(OpCodes.Box, type);
                    }
                    else
                    {
                        il.Emit(OpCodes.Dup);
                    }
                    il.EmitCall(onAfterDeserialize);
                }
            }

            // reader.Depth--;
            reader.EmitLdarg();
            il.Emit(OpCodes.Dup);
            il.EmitCall(readerDepthGet);
            il.Emit(OpCodes.Ldc_I4_1);
            il.Emit(OpCodes.Sub_Ovf);
            il.EmitCall(readerDepthSet);

            if (info.IsStruct)
            {
                il.Emit(OpCodes.Ldloc, structLocal);
            }
        }
Esempio n. 2
0
        static void EmitDeserializeUnionCase(ILGenerator il, Type type, UnionSerializationInfo info, LocalBuilder unionKey, FieldBuilder stringByteKeysField)
        {
            // var startOffset = offset;
            var startOffsetLocal = il.DeclareLocal(typeof(int)); // [loc:0]

            il.EmitLdarg(2);
            il.EmitStloc(startOffsetLocal);

            // var length = ReadMapHeader
            var length = il.DeclareLocal(typeof(int));

            il.EmitLdarg(1);
            il.EmitLdarg(2);
            il.EmitLdarg(4);

            if (info.IsIntKey)
            {
                il.EmitCall(MessagePackBinaryTypeInfo.ReadArrayHeader);
            }
            else
            {
                il.EmitCall(MessagePackBinaryTypeInfo.ReadMapHeader);
            }
            il.EmitStloc(length);
            EmitOffsetPlusReadSize(il);

            // make local fields
            Label?gotoDefault = null;

            DeserializeInfo[] infoList;
            if (info.IsIntKey)
            {
                var maxKey    = info.Members.Select(x => x.IntKey).DefaultIfEmpty(-1).Max();
                var len       = maxKey + 1;
                var intKeyMap = info.Members.ToDictionary(x => x.IntKey);

                infoList = Enumerable.Range(0, len)
                           .Select(x =>
                {
                    UnionSerializationInfo.EmittableMember member;
                    if (intKeyMap.TryGetValue(x, out member))
                    {
                        return(new DeserializeInfo
                        {
                            MemberInfo = member,
                            LocalField = il.DeclareLocal(member.Type),
                            SwitchLabel = il.DefineLabel()
                        });
                    }
                    else
                    {
                        // return null MemberInfo, should filter null
                        if (gotoDefault == null)
                        {
                            gotoDefault = il.DefineLabel();
                        }
                        return(new DeserializeInfo
                        {
                            MemberInfo = null,
                            LocalField = null,
                            SwitchLabel = gotoDefault.Value,
                        });
                    }
                })
                           .ToArray();
            }
            else
            {
                infoList = info.Members
                           .Select(item => new DeserializeInfo
                {
                    MemberInfo  = item,
                    LocalField  = il.DeclareLocal(item.Type),
                    SwitchLabel = il.DefineLabel()
                })
                           .ToArray();
            }

            // Read Loop(for var i = 0; i< length; i++)
            if (info.IsStringKey)
            {
                var automata = new AutomataDictionary();
                for (int i = 0; i < info.Members.Length; i++)
                {
                    automata.Add(info.Members[i].StringKey, i);
                }

                var buffer          = il.DeclareLocal(typeof(byte).MakeByRefType(), true);
                var keyArraySegment = il.DeclareLocal(typeof(ArraySegment <byte>));
                var longKey         = il.DeclareLocal(typeof(ulong));
                var p    = il.DeclareLocal(typeof(byte *));
                var rest = il.DeclareLocal(typeof(int));

                // fixed (byte* buffer = &bytes[0]) {
                il.EmitLdarg(1);
                il.EmitLdc_I4(0);
                il.Emit(OpCodes.Ldelema, typeof(byte));
                il.EmitStloc(buffer);

                // for (int i = 0; i < len; i++)
                il.EmitIncrementFor(length, forILocal =>
                {
                    var readNext = il.DefineLabel();
                    var loopEnd  = il.DefineLabel();

                    il.EmitLdarg(1);
                    il.EmitLdarg(2);
                    il.EmitLdarg(4);
                    il.EmitCall(MessagePackBinaryTypeInfo.ReadStringSegment);
                    il.EmitStloc(keyArraySegment);
                    EmitOffsetPlusReadSize(il);

                    // p = buffer + arraySegment.Offset
                    il.EmitLdloc(buffer);
                    il.Emit(OpCodes.Conv_I);
                    il.EmitLdloca(keyArraySegment);
                    il.EmitCall(typeof(ArraySegment <byte>).GetRuntimeProperty("Offset").GetGetMethod());
                    il.Emit(OpCodes.Add);
                    il.EmitStloc(p);

                    // rest = arraySegment.Count
                    il.EmitLdloca(keyArraySegment);
                    il.EmitCall(typeof(ArraySegment <byte>).GetRuntimeProperty("Count").GetGetMethod());
                    il.EmitStloc(rest);

                    // if(rest == 0) goto End
                    il.EmitLdloc(rest);
                    il.Emit(OpCodes.Brfalse, readNext);

                    // gen automata name lookup
                    automata.EmitMatch(il, p, rest, longKey, x =>
                    {
                        var i = x.Value;
                        if (infoList[i].MemberInfo != null)
                        {
                            EmitDeserializeValue(il, infoList[i]);
                            il.Emit(OpCodes.Br, loopEnd);
                        }
                        else
                        {
                            il.Emit(OpCodes.Br, readNext);
                        }
                    }, () =>
                    {
                        il.Emit(OpCodes.Br, readNext);
                    });

                    il.MarkLabel(readNext);
                    il.EmitLdarg(4);
                    il.EmitLdarg(1);
                    il.EmitLdarg(2);
                    il.EmitCall(MessagePackBinaryTypeInfo.ReadNextBlock);
                    il.Emit(OpCodes.Stind_I4);

                    il.MarkLabel(loopEnd);
                    EmitOffsetPlusReadSize(il);
                });

                // end fixed
                il.Emit(OpCodes.Ldc_I4_0);
                il.Emit(OpCodes.Conv_U);
                il.EmitStloc(buffer);
            }
            else
            {
                var key           = il.DeclareLocal(typeof(int));
                var switchDefault = il.DefineLabel();

                il.EmitIncrementFor(length, forILocal =>
                {
                    var loopEnd = il.DefineLabel();

                    il.EmitLdloc(forILocal);
                    il.EmitStloc(key);

                    // switch... local = Deserialize
                    il.EmitLdloc(key);

                    il.Emit(OpCodes.Switch, infoList.Select(x => x.SwitchLabel).ToArray());

                    il.MarkLabel(switchDefault);
                    // default, only read. readSize = MessagePackBinary.ReadNextBlock(bytes, offset);
                    il.EmitLdarg(4);
                    il.EmitLdarg(1);
                    il.EmitLdarg(2);
                    il.EmitCall(MessagePackBinaryTypeInfo.ReadNextBlock);
                    il.Emit(OpCodes.Stind_I4);
                    il.Emit(OpCodes.Br, loopEnd);

                    if (gotoDefault != null)
                    {
                        il.MarkLabel(gotoDefault.Value);
                        il.Emit(OpCodes.Br, switchDefault);
                    }

                    foreach (var item in infoList)
                    {
                        if (item.MemberInfo != null)
                        {
                            il.MarkLabel(item.SwitchLabel);
                            EmitDeserializeValue(il, item);
                            il.Emit(OpCodes.Br, loopEnd);
                        }
                    }

                    // offset += readSize
                    il.MarkLabel(loopEnd);
                    EmitOffsetPlusReadSize(il);
                });
            }

            // finish readSize: readSize = offset - startOffset;
            il.EmitLdarg(4);
            il.EmitLdarg(2);
            il.EmitLdloc(startOffsetLocal);
            il.Emit(OpCodes.Sub);
            il.Emit(OpCodes.Stind_I4);

            // create result union case
            var structLocal = EmitNewObject(il, type, info, infoList);

            // IMessagePackSerializationCallbackReceiver.OnAfterDeserialize()
            if (type.GetTypeInfo().ImplementedInterfaces.Any(x => x == typeof(IMessagePackSerializationCallbackReceiver)))
            {
                // call directly
                var runtimeMethods = type.GetRuntimeMethods().Where(x => x.Name == "OnAfterDeserialize").ToArray();
                if (runtimeMethods.Length == 1)
                {
                    if (info.IsClass)
                    {
                        il.Emit(OpCodes.Dup);
                    }
                    else
                    {
                        il.EmitLdloca(structLocal);
                    }

                    il.Emit(OpCodes.Call, runtimeMethods[0]); // don't use EmitCall helper(must use 'Call')
                }
                else
                {
                    if (info.IsStruct)
                    {
                        il.EmitLdloc(structLocal);
                        il.Emit(OpCodes.Box, type);
                    }
                    else
                    {
                        il.Emit(OpCodes.Dup);
                    }
                    il.EmitCall(onAfterDeserialize);
                }
            }

            if (info.IsStruct)
            {
                il.Emit(OpCodes.Ldloc, structLocal);
            }
        }