static LocalBuilder EmitNewObject(ILGenerator il, Type type, ObjectSerializationInfo info, DeserializeInfo[] members)
        {
            if (info.IsClass)
            {
                foreach (var item in info.ConstructorParameters)
                {
                    var local = members.First(x => x.MemberInfo == item);
                    il.EmitLdloc(local.LocalField);
                }
                il.Emit(OpCodes.Newobj, info.BestmatchConstructor);

                foreach (var item in members.Where(x => x.MemberInfo != null && x.MemberInfo.IsWritable))
                {
                    il.Emit(OpCodes.Dup);
                    il.EmitLdloc(item.LocalField);
                    item.MemberInfo.EmitStoreValue(il);
                }

                return(null);
            }
            else
            {
                var result = il.DeclareLocal(type);
                if (info.BestmatchConstructor == null)
                {
                    il.Emit(OpCodes.Ldloca, result);
                    il.Emit(OpCodes.Initobj, type);
                }
                else
                {
                    foreach (var item in info.ConstructorParameters)
                    {
                        var local = members.First(x => x.MemberInfo == item);
                        il.EmitLdloc(local.LocalField);
                    }
                    il.Emit(OpCodes.Newobj, info.BestmatchConstructor);
                    il.Emit(OpCodes.Stloc, result);
                }

                foreach (var item in members.Where(x => x.MemberInfo != null && x.MemberInfo.IsWritable))
                {
                    il.EmitLdloca(result);
                    il.EmitLdloc(item.LocalField);
                    item.MemberInfo.EmitStoreValue(il);
                }

                return(result); // struct returns local result field
            }
        }
        static void BuildConstructor(Type type, ObjectSerializationInfo info, ConstructorInfo method, FieldBuilder dictionaryField, ILGenerator il)
        {
            il.EmitLdarg(0);
            il.Emit(OpCodes.Call, objectCtor);

            il.EmitLdarg(0);
            il.EmitLdc_I4(info.Members.Length);
            il.Emit(OpCodes.Newobj, dictionaryConstructor);

            foreach (var item in info.Members)
            {
                il.Emit(OpCodes.Dup);
                il.Emit(OpCodes.Ldstr, item.StringKey);
                il.EmitLdc_I4(item.IntKey);
                il.EmitCall(dictionaryAdd);
            }

            il.Emit(OpCodes.Stfld, dictionaryField);
            il.Emit(OpCodes.Ret);
        }
        // T Deserialize([arg:1]byte[] bytes, [arg:2]int offset, [arg:3]IFormatterResolver formatterResolver, [arg:4]out int readSize);
        static void BuildDeserialize(Type type, ObjectSerializationInfo info, MethodBuilder method, FieldBuilder dictionaryField, ILGenerator il)
        {
            // if(MessagePackBinary.IsNil) readSize = 1, return null;
            var falseLabel = il.DefineLabel();

            il.EmitLdarg(1);
            il.EmitLdarg(2);
            il.EmitCall(MessagePackBinaryTypeInfo.IsNil);
            il.Emit(OpCodes.Brfalse_S, falseLabel);
            if (type.GetTypeInfo().IsClass)
            {
                il.EmitLdarg(4);
                il.EmitLdc_I4(1);
                il.Emit(OpCodes.Stind_I4);
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Ret);
            }
            else
            {
                il.Emit(OpCodes.Ldstr, "typecode is null, struct not supported");
                il.Emit(OpCodes.Newobj, invalidOperationExceptionConstructor);
                il.Emit(OpCodes.Throw);
            }

            // var startOffset = offset;
            il.MarkLabel(falseLabel);
            var startOffsetLocal = il.DeclareLocal(typeof(int)); // [loc:0]

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

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

            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(0).Max();
                var len       = maxKey + 1;
                var intKeyMap = info.Members.ToDictionary(x => x.IntKey);

                infoList = Enumerable.Range(0, len)
                           .Select(x =>
                {
                    ObjectSerializationInfo.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++)
            {
                var key           = il.DeclareLocal(typeof(int));
                var switchDefault = il.DefineLabel();
                var loopEnd       = il.DefineLabel();
                var stringKeyTrue = il.DefineLabel();
                il.EmitIncrementFor(length, forILocal =>
                {
                    if (info.IsStringKey)
                    {
                        // get string key -> dictionary lookup
                        il.EmitLdarg(0);
                        il.Emit(OpCodes.Ldfld, dictionaryField);
                        il.EmitLdarg(1);
                        il.EmitLdarg(2);
                        il.EmitLdarg(4);
                        il.EmitCall(MessagePackBinaryTypeInfo.ReadString);
                        il.EmitLdloca(key);
                        il.EmitCall(dictionaryTryGetValue);
                        EmitOffsetPlusReadSize(il);
                        il.Emit(OpCodes.Brtrue_S, stringKeyTrue);

                        il.EmitLdarg(4);
                        il.EmitLdarg(1);
                        il.EmitLdarg(2);
                        il.EmitCall(MessagePackBinaryTypeInfo.ReadNextBlock);
                        il.Emit(OpCodes.Stind_I4);
                        il.Emit(OpCodes.Br, loopEnd);

                        il.MarkLabel(stringKeyTrue);
                    }
                    else
                    {
                        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 object
            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);
            }


            il.Emit(OpCodes.Ret);
        }
        // int Serialize([arg:1]ref byte[] bytes, [arg:2]int offset, [arg:3]T value, [arg:4]IFormatterResolver formatterResolver);
        static void BuildSerialize(Type type, ObjectSerializationInfo info, MethodBuilder method, ILGenerator il)
        {
            // if(value == null) return WriteNil
            if (type.GetTypeInfo().IsClass)
            {
                var elseBody = il.DefineLabel();

                il.EmitLdarg(3);
                il.Emit(OpCodes.Brtrue_S, elseBody);
                il.EmitLdarg(1);
                il.EmitLdarg(2);
                il.EmitCall(MessagePackBinaryTypeInfo.WriteNil);
                il.Emit(OpCodes.Ret);

                il.MarkLabel(elseBody);
            }

            // IMessagePackSerializationCallbackReceiver.OnBeforeSerialize()
            if (type.GetTypeInfo().ImplementedInterfaces.Any(x => x == typeof(IMessagePackSerializationCallbackReceiver)))
            {
                // call directly
                var runtimeMethods = type.GetRuntimeMethods().Where(x => x.Name == "OnBeforeSerialize").ToArray();
                if (runtimeMethods.Length == 1)
                {
                    if (info.IsStruct)
                    {
                        il.EmitLdarga(3);
                    }
                    else
                    {
                        il.EmitLdarg(3);
                    }
                    il.Emit(OpCodes.Call, runtimeMethods[0]); // don't use EmitCall helper(must use 'Call')
                }
                else
                {
                    il.EmitLdarg(3);
                    if (info.IsStruct)
                    {
                        il.Emit(OpCodes.Box, type);
                    }
                    il.EmitCall(onBeforeSerialize);
                }
            }

            // var startOffset = offset;
            var startOffsetLocal = il.DeclareLocal(typeof(int)); // [loc:0]

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

            if (info.IsIntKey)
            {
                // use Array
                var maxKey    = info.Members.Where(x => x.IsReadable).Select(x => x.IntKey).DefaultIfEmpty(0).Max();
                var intKeyMap = info.Members.Where(x => x.IsReadable).ToDictionary(x => x.IntKey);

                EmitOffsetPlusEqual(il, null, () =>
                {
                    var len = maxKey + 1;
                    il.EmitLdc_I4(len);
                    if (len <= MessagePackRange.MaxFixArrayCount)
                    {
                        il.EmitCall(MessagePackBinaryTypeInfo.WriteFixedArrayHeaderUnsafe);
                    }
                    else
                    {
                        il.EmitCall(MessagePackBinaryTypeInfo.WriteArrayHeader);
                    }
                });

                for (int i = 0; i <= maxKey; i++)
                {
                    ObjectSerializationInfo.EmittableMember member;
                    if (intKeyMap.TryGetValue(i, out member))
                    {
                        // offset += serialzie
                        EmitSerializeValue(il, type.GetTypeInfo(), member);
                    }
                    else
                    {
                        // Write Nil as Blanc
                        EmitOffsetPlusEqual(il, null, () =>
                        {
                            il.EmitCall(MessagePackBinaryTypeInfo.WriteNil);
                        });
                    }
                }
            }
            else
            {
                // use Map
                var writeCount = info.Members.Count(x => x.IsReadable);

                EmitOffsetPlusEqual(il, null, () =>
                {
                    il.EmitLdc_I4(writeCount);
                    if (writeCount <= MessagePackRange.MaxFixMapCount)
                    {
                        il.EmitCall(MessagePackBinaryTypeInfo.WriteFixedMapHeaderUnsafe);
                    }
                    else
                    {
                        il.EmitCall(MessagePackBinaryTypeInfo.WriteMapHeader);
                    }
                });

                foreach (var item in info.Members.Where(x => x.IsReadable))
                {
                    // offset += writekey
                    if (info.IsStringKey)
                    {
                        EmitOffsetPlusEqual(il, null, () =>
                        {
                            // embed string and bytesize
                            il.Emit(OpCodes.Ldstr, item.StringKey);
                            il.EmitLdc_I4(StringEncoding.UTF8.GetByteCount(item.StringKey));
                            il.EmitCall(MessagePackBinaryTypeInfo.WriteStringUnsafe);
                        });
                    }

                    // offset += serialzie
                    EmitSerializeValue(il, type.GetTypeInfo(), item);
                }
            }

            // return startOffset- offset;
            il.EmitLdarg(2);
            il.EmitLdloc(startOffsetLocal);
            il.Emit(OpCodes.Sub);
            il.Emit(OpCodes.Ret);
        }