private static LocalBuilder EmitNewObject(ILGenerator il, Type type, UnionSerializationInfo info, DeserializeInfo[] members)
        {
            if (info.IsClass)
            {
                foreach (UnionSerializationInfo.EmittableMember item in info.MethodParameters)
                {
                    DeserializeInfo local = members.First(x => x.MemberInfo == item);
                    il.EmitLdloc(local.LocalField);
                }

                il.Emit(OpCodes.Call, info.NewMethod);

                return(null);
            }
            else
            {
                LocalBuilder result = il.DeclareLocal(type);
                foreach (var item in info.MethodParameters)
                {
                    var local = members.First(x => x.MemberInfo == item);
                    il.EmitLdloc(local.LocalField);
                }

                il.Emit(OpCodes.Call, info.NewMethod);
                il.Emit(OpCodes.Stloc, result);

                return(result); // struct returns local result field
            }
        }
        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);
            }
        }
        static void EmitSerializeUnionCase(
            ILGenerator il,
            Type type,
            TypeInfo ti,
            UnionSerializationInfo info,
            FieldBuilder stringByteKeysField,
            ArgumentField argWriter,
            ArgumentField argValue,
            ArgumentField argOptions,
            LocalBuilder localResolver
            )
        {
            if (info.IsIntKey)
            {
                // use Array
                var maxKey    = info.Members.Select(x => x.IntKey).DefaultIfEmpty(-1).Max();
                var intKeyMap = info.Members.ToDictionary(x => x.IntKey);

                var len = maxKey + 1;
                argWriter.EmitLoad();
                il.EmitLdc_I4(len);
                il.EmitCall(MessagePackWriterTypeInfo.WriteArrayHeader);

                var index = 0;
                for (int i = 0; i <= maxKey; i++)
                {
                    UnionSerializationInfo.EmittableMember member;
                    if (intKeyMap.TryGetValue(i, out member))
                    {
                        EmitSerializeValue(il, ti, member, index++, argWriter, argValue, argOptions, localResolver);
                    }
                    else
                    {
                        // Write Nil as Blanc
                        argWriter.EmitLoad();
                        il.EmitCall(MessagePackWriterTypeInfo.WriteNil);
                    }
                }
            }
            else
            {
                // use Map
                var writeCount = info.Members.Count();

                argWriter.EmitLoad();
                il.EmitLdc_I4(writeCount);
                ////if (writeCount <= MessagePackRange.MaxFixMapCount)
                ////{
                ////    il.EmitCall(MessagePackWriterTypeInfo.WriteFixedMapHeaderUnsafe);
                ////}
                ////else
                {
                    il.EmitCall(MessagePackWriterTypeInfo.WriteMapHeader);
                }

                var index = 0;
                foreach (UnionSerializationInfo.EmittableMember item in info.Members)
                {
                    argWriter.EmitLoad();
                    il.EmitLoadThis();
                    il.EmitLdfld(stringByteKeysField);
                    il.EmitLdc_I4(index);
                    il.Emit(OpCodes.Ldelem_Ref);
                    il.Emit(OpCodes.Call, ReadOnlySpanFromByteArray); // convert byte[] to ReadOnlySpan<byte>

                    // Optimize, WriteRaw(Unity, large) or UnsafeMemory32/64.WriteRawX
                    var valueLen = CodeGenHelpers.GetEncodedStringBytes(item.StringKey).Length;
                    if (valueLen <= MessagePackRange.MaxFixStringLength)
                    {
                        if (UnsafeMemory.Is32Bit)
                        {
                            il.EmitCall(typeof(UnsafeMemory32).GetRuntimeMethod("WriteRaw" + valueLen, new[] { typeof(MessagePackWriter).MakeByRefType(), typeof(ReadOnlySpan <byte>) }));
                        }
                        else
                        {
                            il.EmitCall(typeof(UnsafeMemory64).GetRuntimeMethod("WriteRaw" + valueLen, new[] { typeof(MessagePackWriter).MakeByRefType(), typeof(ReadOnlySpan <byte>) }));
                        }
                    }
                    else
                    {
                        il.EmitCall(MessagePackWriterTypeInfo.WriteRaw);
                    }

                    EmitSerializeValue(il, type.GetTypeInfo(), item, index, argWriter, argValue, argOptions, localResolver);
                    index++;
                }
            }
        }
        // T Deserialize([arg:1]ref MessagePackReader reader, [arg:2]MessagePackSerializerOptions options);
        static void BuildDeserialize(
            Type type,
            UnionCaseInfo[] infos,
            MethodBuilder method,
            FieldBuilder[] stringByteKeysFields,
            ILGenerator il,
            int firstArgIndex
            )
        {
            var ti = type.GetTypeInfo();

            var reader     = new ArgumentField(il, firstArgIndex, @ref: true);
            var argOptions = new ArgumentField(il, firstArgIndex + 1);

            // if(MessagePackBinary.TryReadNil()) { return null; }
            Label falseLabel = il.DefineLabel();

            reader.EmitLdarg();
            il.EmitCall(MessagePackReaderTypeInfo.TryReadNil);
            il.Emit(OpCodes.Brfalse_S, falseLabel);

            if (ti.IsClass)
            {
                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);
            }

            il.MarkLabel(falseLabel);

            // IFormatterResolver resolver = options.Resolver;
            LocalBuilder localResolver = il.DeclareLocal(typeof(IFormatterResolver));

            argOptions.EmitLdarg();
            il.EmitCall(getResolverFromOptions);
            il.EmitStloc(localResolver);

            // read-array header and validate, reader.ReadArrayHeader() != 2) throw;
            Label rightLabel = il.DefineLabel();

            reader.EmitLdarg();
            il.EmitCall(MessagePackReaderTypeInfo.ReadArrayHeader);
            il.EmitLdc_I4(2);
            il.Emit(OpCodes.Beq_S, rightLabel);
            il.Emit(OpCodes.Ldstr, "Invalid Union data was detected. Type:" + type.FullName);
            il.Emit(OpCodes.Newobj, invalidOperationExceptionConstructor);
            il.Emit(OpCodes.Throw);

            il.MarkLabel(rightLabel);

            // read key
            LocalBuilder key = il.DeclareLocal(typeof(int));

            reader.EmitLdarg();
            il.EmitCall(MessagePackReaderTypeInfo.ReadInt32);
            il.EmitStloc(key);

            // switch->read
            LocalBuilder result  = il.DeclareLocal(type);
            Label        loopEnd = il.DefineLabel();

            if (ti.IsClass)
            {
                il.Emit(OpCodes.Ldnull);
                il.EmitStloc(result);
            }
            il.Emit(OpCodes.Ldloc, key);

            var switchLabels = infos.Select(x => new { Label = il.DefineLabel(), Info = x }).ToArray();

            il.Emit(OpCodes.Switch, switchLabels.Select(x => x.Label).ToArray());

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

            foreach (var item in switchLabels)
            {
                il.MarkLabel(item.Label);
                EmitDeserializeUnionCase(
                    il,
                    type,
                    UnionSerializationInfo.CreateOrNull(type, item.Info),
                    key,
                    stringByteKeysFields[item.Info.Tag],
                    reader,
                    argOptions,
                    localResolver
                    );
                il.Emit(OpCodes.Stloc, result);
                il.Emit(OpCodes.Br, loopEnd);
            }

            il.MarkLabel(loopEnd);

            il.Emit(OpCodes.Ldloc, result);
            il.Emit(OpCodes.Ret);
        }
        // void Serialize(ref [arg:1]MessagePackWriter writer, [arg:2]T value, [arg:3]MessagePackSerializerOptions options);
        static void BuildSerialize(
            Type type,
            UnionCaseInfo[] infos,
            MethodBuilder method,
            FieldBuilder[] stringByteKeysFields,
            ILGenerator il,
            int firstArgIndex
            )
        {
            var tag = getTag(type);
            var ti  = type.GetTypeInfo();

            var argWriter  = new ArgumentField(il, firstArgIndex);
            var argValue   = new ArgumentField(il, firstArgIndex + 1, type);
            var argOptions = new ArgumentField(il, firstArgIndex + 2);

            // if(value == null) return WriteNil
            if (ti.IsClass)
            {
                Label elseBody = il.DefineLabel();

                argValue.EmitLoad();
                il.Emit(OpCodes.Brtrue_S, elseBody);
                argWriter.EmitLoad();
                il.EmitCall(MessagePackWriterTypeInfo.WriteNil);
                il.Emit(OpCodes.Ret);

                il.MarkLabel(elseBody);
            }

            // IMessagePackSerializationCallbackReceiver.OnBeforeSerialize()
            if (ti.ImplementedInterfaces.Any(x => x == typeof(IMessagePackSerializationCallbackReceiver)))
            {
                // call directly
                MethodInfo[] runtimeMethods = type.GetRuntimeMethods().Where(x => x.Name == "OnBeforeSerialize").ToArray();
                if (runtimeMethods.Length == 1)
                {
                    argValue.EmitLoad();
                    il.Emit(OpCodes.Call, runtimeMethods[0]); // don't use EmitCall helper(must use 'Call')
                }
                else
                {
                    argValue.EmitLdarg(); // force ldarg
                    il.EmitBoxOrDoNothing(type);
                    il.EmitCall(onBeforeSerialize);
                }
            }

            // IFormatterResolver resolver = options.Resolver;
            LocalBuilder localResolver = il.DeclareLocal(typeof(IFormatterResolver));

            argOptions.EmitLoad();
            il.EmitCall(getResolverFromOptions);
            il.EmitStloc(localResolver);

            // writer.WriteArrayHeader(2, false);
            argWriter.EmitLdarg();
            il.EmitLdc_I4(2);
            il.EmitCall(MessagePackWriterTypeInfo.WriteArrayHeader);

            // writer.Write(value.Tag)
            argWriter.EmitLdarg();
            if (ti.IsClass)
            {
                argValue.EmitLdarg();
            }
            else
            {
                argValue.EmitLdarga();
            }
            il.EmitCall(tag);
            il.EmitCall(MessagePackWriterTypeInfo.WriteInt32);

            var loopEnd = il.DefineLabel();

            // switch-case (offset += resolver.GetFormatter.Serialize(with cast)
            var switchLabels = infos.Select(x => new { Label = il.DefineLabel(), Info = x }).ToArray();

            if (ti.IsClass)
            {
                argValue.EmitLdarg();
            }
            else
            {
                argValue.EmitLdarga();
            }
            il.EmitCall(tag);
            il.Emit(OpCodes.Switch, switchLabels.Select(x => x.Label).ToArray());
            il.Emit(OpCodes.Br, loopEnd); // default

            foreach (var item in switchLabels)
            {
                il.MarkLabel(item.Label);
                EmitSerializeUnionCase(
                    il,
                    type,
                    ti,
                    UnionSerializationInfo.CreateOrNull(type, item.Info),
                    stringByteKeysFields[item.Info.Tag],
                    argWriter,
                    argValue,
                    argOptions,
                    localResolver
                    );
                il.Emit(OpCodes.Br, loopEnd);
            }

            // return;
            il.MarkLabel(loopEnd);
            il.Emit(OpCodes.Ret);
        }
Exemplo n.º 6
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);
            }
        }
Exemplo n.º 7
0
        // T Deserialize([arg:1]byte[] bytes, [arg:2]int offset, [arg:3]IFormatterResolver formatterResolver, [arg:4]out int readSize);
        static void BuildDeserialize(Type type, Microsoft.FSharp.Reflection.UnionCaseInfo[] infos, MethodBuilder method, FieldBuilder[] stringByteKeysFields, ILGenerator il)
        {
            var ti = type.GetTypeInfo();

            // 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 (ti.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);
            }

            // read-array header and validate, ReadArrayHeader(bytes, offset, out readSize) != 2) throw;
            il.MarkLabel(falseLabel);
            var startOffset = il.DeclareLocal(typeof(int));

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

            var rightLabel = il.DefineLabel();

            il.EmitLdarg(1);
            il.EmitLdarg(2);
            il.EmitLdarg(4);
            il.EmitCall(MessagePackBinaryTypeInfo.ReadArrayHeader);
            il.EmitLdc_I4(2);
            il.Emit(OpCodes.Beq_S, rightLabel);
            il.Emit(OpCodes.Ldstr, "Invalid Union data was detected. Type:" + type.FullName);
            il.Emit(OpCodes.Newobj, invalidOperationExceptionConstructor);
            il.Emit(OpCodes.Throw);

            il.MarkLabel(rightLabel);
            EmitOffsetPlusReadSize(il);

            // read key
            var key = il.DeclareLocal(typeof(int));

            il.EmitLdarg(1);
            il.EmitLdarg(2);
            il.EmitLdarg(4);
            il.EmitCall(MessagePackBinaryTypeInfo.ReadInt32);
            il.EmitStloc(key);
            EmitOffsetPlusReadSize(il);

            // switch->read
            var result  = il.DeclareLocal(type);
            var loopEnd = il.DefineLabel();

            if (ti.IsClass)
            {
                il.Emit(OpCodes.Ldnull);
                il.EmitStloc(result);
            }
            il.Emit(OpCodes.Ldloc, key);

            var switchLabels = infos.Select(x => new { Label = il.DefineLabel(), Info = x }).ToArray();

            il.Emit(OpCodes.Switch, switchLabels.Select(x => x.Label).ToArray());

            // default
            il.EmitLdarg(2);
            il.EmitLdarg(1);
            il.EmitLdarg(2);
            il.EmitCall(MessagePackBinaryTypeInfo.ReadNextBlock);
            il.Emit(OpCodes.Add);
            il.EmitStarg(2);
            il.Emit(OpCodes.Br, loopEnd);

            foreach (var item in switchLabels)
            {
                il.MarkLabel(item.Label);
                EmitDeserializeUnionCase(il, type, UnionSerializationInfo.CreateOrNull(type, item.Info), key, stringByteKeysFields[item.Info.Tag]);
                il.Emit(OpCodes.Stloc, result);
                il.Emit(OpCodes.Br, loopEnd);
            }

            il.MarkLabel(loopEnd);

            // finish readSize = offset - startOffset;
            il.EmitLdarg(4);
            il.EmitLdarg(2);
            il.EmitLdloc(startOffset);
            il.Emit(OpCodes.Sub);
            il.Emit(OpCodes.Stind_I4);
            il.Emit(OpCodes.Ldloc, result);
            il.Emit(OpCodes.Ret);
        }
Exemplo n.º 8
0
        static void EmitSerializeUnionCase(ILGenerator il, Type type, TypeInfo ti, UnionSerializationInfo info, FieldBuilder stringByteKeysField)
        {
            // IMessagePackSerializationCallbackReceiver.OnBeforeSerialize()
            if (ti.ImplementedInterfaces.Any(x => x == typeof(IMessagePackSerializationCallbackReceiver)))
            {
                // call directly
                var runtimeMethods = type.GetRuntimeMethods().Where(x => x.Name == "OnBeforeSerialize").ToArray();
                if (runtimeMethods.Length == 1)
                {
                    il.EmitLoadArg(ti, 3);
                    il.Emit(OpCodes.Call, runtimeMethods[0]); // don't use EmitCall helper(must use 'Call')
                }
                else
                {
                    il.EmitLoadArg(ti, 3);
                    if (info.IsStruct)
                    {
                        il.Emit(OpCodes.Box, type);
                    }
                    il.EmitCall(onBeforeSerialize);
                }
            }

            if (info.IsIntKey)
            {
                // use Array
                var maxKey    = info.Members.Select(x => x.IntKey).DefaultIfEmpty(0).Max();
                var intKeyMap = info.Members.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++)
                {
                    UnionSerializationInfo.EmittableMember member;
                    if (intKeyMap.TryGetValue(i, out member))
                    {
                        // offset += serialzie
                        EmitSerializeValue(il, ti, member);
                    }
                    else
                    {
                        // Write Nil as Blanc
                        EmitOffsetPlusEqual(il, null, () =>
                        {
                            il.EmitCall(MessagePackBinaryTypeInfo.WriteNil);
                        });
                    }
                }
            }
            else
            {
                // use Map
                var writeCount = info.Members.Count();

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

                var index = 0;
                foreach (var item in info.Members)
                {
                    // offset += writekey
                    EmitOffsetPlusEqual(il, null, () =>
                    {
                        il.EmitLdarg(0);
                        il.EmitLdfld(stringByteKeysField);
                        il.EmitLdc_I4(index);
                        il.Emit(OpCodes.Ldelem_Ref);

                        // Optimize, WriteRaw(Unity, large) or UnsafeMemory32/64.WriteRawX
#if NETSTANDARD
                        var valueLen = MessagePackBinary.GetEncodedStringBytes(item.StringKey).Length;
                        if (valueLen <= MessagePackRange.MaxFixStringLength)
                        {
                            if (UnsafeMemory.Is32Bit)
                            {
                                il.EmitCall(typeof(UnsafeMemory32).GetRuntimeMethod("WriteRaw" + valueLen, new[] { refByte, typeof(int), typeof(byte[]) }));
                            }
                            else
                            {
                                il.EmitCall(typeof(UnsafeMemory64).GetRuntimeMethod("WriteRaw" + valueLen, new[] { refByte, typeof(int), typeof(byte[]) }));
                            }
                        }
                        else
#endif
                        {
                            il.EmitCall(MessagePackBinaryTypeInfo.WriteRaw);
                        }
                        index++;
                    });

                    // offset += serialzie
                    EmitSerializeValue(il, ti, item);
                }
            }
        }
Exemplo n.º 9
0
        // int Serialize([arg:1]ref byte[] bytes, [arg:2]int offset, [arg:3]T value, [arg:4]IFormatterResolver formatterResolver);
        static void BuildSerialize(Type type, Microsoft.FSharp.Reflection.UnionCaseInfo[] infos, MethodBuilder method, FieldBuilder[] stringByteKeysFields, ILGenerator il)
        {
            var tag = getTag(type);
            var ti  = type.GetTypeInfo();

            // if(value == null) return WriteNil
            var elseBody     = il.DefineLabel();
            var notFoundType = il.DefineLabel();

            il.EmitLoadArg(ti, 3);
            il.Emit(OpCodes.Brtrue_S, elseBody);
            il.Emit(OpCodes.Br, notFoundType);
            il.MarkLabel(elseBody);

            // var startOffset = offset;
            var startOffsetLocal = il.DeclareLocal(typeof(int));

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

            // offset += WriteFixedArrayHeaderUnsafe(,,2);
            EmitOffsetPlusEqual(il, null, () =>
            {
                il.EmitLdc_I4(2);
                il.EmitCall(MessagePackBinaryTypeInfo.WriteFixedArrayHeaderUnsafe);
            });

            // offset += WriteInt32(,,value.Tag)
            EmitOffsetPlusEqual(il, null, () =>
            {
                il.EmitLoadArg(ti, 3);
                il.EmitCall(tag);
                il.EmitCall(MessagePackBinaryTypeInfo.WriteInt32);
            });

            var loopEnd = il.DefineLabel();

            // switch-case (offset += resolver.GetFormatter.Serialize(with cast)
            var switchLabels = infos.Select(x => new { Label = il.DefineLabel(), Info = x }).ToArray();

            il.EmitLoadArg(ti, 3);
            il.EmitCall(tag);
            il.Emit(OpCodes.Switch, switchLabels.Select(x => x.Label).ToArray());
            il.Emit(OpCodes.Br, loopEnd); // default

            foreach (var item in switchLabels)
            {
                il.MarkLabel(item.Label);
                EmitSerializeUnionCase(il, type, ti, UnionSerializationInfo.CreateOrNull(type, item.Info), stringByteKeysFields[item.Info.Tag]);
                il.Emit(OpCodes.Br, loopEnd);
            }

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

            // else, return WriteNil
            il.MarkLabel(notFoundType);
            il.EmitLdarg(1);
            il.EmitLdarg(2);
            il.EmitCall(MessagePackBinaryTypeInfo.WriteNil);
            il.Emit(OpCodes.Ret);
        }