static void EmitDeserializeValue(
            ILGenerator il,
            DeserializeInfo info,
            int index,
            ArgumentField argReader,
            ArgumentField argOptions,
            LocalBuilder localResolver
            )
        {
            Label storeLabel = il.DefineLabel();

            UnionSerializationInfo.EmittableMember member = info.MemberInfo;
            Type t = member.Type;

            if (IsOptimizeTargetType(t))
            {
                if (!t.GetTypeInfo().IsValueType)
                {
                    // As a nullable type (e.g. byte[] and string) we need to first call TryReadNil
                    // if (reader.TryReadNil())
                    Label readNonNilValueLabel = il.DefineLabel();
                    argReader.EmitLdarg();
                    il.EmitCall(MessagePackReaderTypeInfo.TryReadNil);
                    il.Emit(OpCodes.Brfalse_S, readNonNilValueLabel);
                    il.Emit(OpCodes.Ldnull);
                    il.Emit(OpCodes.Br, storeLabel);

                    il.MarkLabel(readNonNilValueLabel);
                }

                argReader.EmitLdarg();
                if (t == typeof(byte[]))
                {
                    LocalBuilder local = il.DeclareLocal(typeof(ReadOnlySequence <byte>?));
                    il.EmitCall(MessagePackReaderTypeInfo.ReadBytes);
                    il.EmitStloc(local);
                    il.EmitLdloca(local);
                    il.EmitCall(ArrayFromNullableReadOnlySequence);
                }
                else
                {
                    il.EmitCall(MessagePackReaderTypeInfo.TypeInfo.GetDeclaredMethods("Read" + t.Name).First(x => x.GetParameters().Length == 0));
                }
            }
            else
            {
                il.EmitLdloc(localResolver);
                il.EmitCall(getFormatterWithVerify.MakeGenericMethod(t));
                argReader.EmitLdarg();
                argOptions.EmitLoad();
                il.EmitCall(getDeserialize(t));
            }

            il.MarkLabel(storeLabel);
            il.EmitStloc(info.LocalField);
        }
Beispiel #2
0
        public static ArgumentField GetArguments(this string args)
        {
            string[]      tempArg = args.Split(' ');
            ArgumentField f       = new ArgumentField();

            foreach (string x in tempArg)
            {
                f.arguments.Add(x);
            }

            return(f);
        }
Beispiel #3
0
        static void BuildMap <TFrom, TTo>(ILGenerator il, MappingInfo <TFrom, TTo> mappingInfo, int firstArgIndex, Action emitBeforeMapLoadDelegate, Action emitAfterMapLoadDelegate, Dictionary <MetaMemberPair <TFrom, TTo>, FieldBuilder> convertActionDictionary)
        {
            var fromType = typeof(TFrom);
            var toType   = typeof(TTo);

            var argFrom     = new ArgumentField(il, firstArgIndex, fromType);
            var argResolver = new ArgumentField(il, firstArgIndex + 1);

            // call beforeMap
            if (mappingInfo.BeforeMap != null)
            {
                emitBeforeMapLoadDelegate();
                argFrom.EmitLdarg();
                il.EmitCall(EmitInfo.GetActionInvoke <TFrom>());
            }

            // if(from == null) return null
            if (!fromType.IsValueType)
            {
                var gotoNextLabel = il.DefineLabel();
                argFrom.EmitLoad();
                il.Emit(OpCodes.Brtrue_S, gotoNextLabel);
                il.Emit(OpCodes.Ldnull);
                il.Emit(OpCodes.Ret);
                il.MarkLabel(gotoNextLabel);
            }

            // construct totype
            var result = EmitNewObject <TFrom, TTo>(il, argFrom, mappingInfo.TargetConstructor);

            // map from -> to.

            foreach (var item in mappingInfo.TargetMembers)
            {
                EmitMapMember(il, item, argFrom, argResolver, result, convertActionDictionary);
            }

            // call afterMap
            if (mappingInfo.AfterMap != null)
            {
                emitAfterMapLoadDelegate();
                il.EmitLdloc(result);
                il.EmitCall(EmitInfo.GetActionInvoke <TTo>());
            }

            // end.
            il.EmitLdloc(result);
            il.Emit(OpCodes.Ret);
        }
Beispiel #4
0
        static LocalBuilder EmitNewObject <TFrom, TTo>(ILGenerator il, ArgumentField fromValue, MetaConstructorInfo <TFrom, TTo> metaConstructorInfo)
        {
            var toType = typeof(TTo);
            var result = il.DeclareLocal(toType);

            if (toType.IsClass)
            {
                if (metaConstructorInfo == null || metaConstructorInfo.ConstructorInfo == null)
                {
                    throw new InvalidOperationException("ConstructorInfo does not find, " + typeof(TFrom) + " -> " + typeof(TTo) + " Mapper.");
                }

                foreach (var item in metaConstructorInfo.Arguments)
                {
                    fromValue.EmitLoad();
                    item.EmitLoadValue(il);
                }
                il.Emit(OpCodes.Newobj, metaConstructorInfo.ConstructorInfo);
                il.EmitStloc(result);
            }
            else
            {
                if (metaConstructorInfo == null || metaConstructorInfo.ConstructorInfo == null)
                {
                    il.Emit(OpCodes.Ldloca, result);
                    il.Emit(OpCodes.Initobj, toType);
                }
                else
                {
                    foreach (var item in metaConstructorInfo.Arguments)
                    {
                        fromValue.EmitLoad();
                        item.EmitLoadValue(il);
                    }
                    il.Emit(OpCodes.Newobj, metaConstructorInfo.ConstructorInfo);
                    il.EmitStloc(result);
                }
            }

            return(result);
        }
        // T Deserialize([arg:1]ref MessagePackReader reader, [arg:2]IFormatterResolver formatterResolver);
        static void BuildDeserialize(Type type, UnionAttribute[] infos, MethodBuilder method, FieldBuilder keyToJumpMap, ILGenerator il)
        {
            // if(MessagePackBinary.TryReadNil()) { return null; }
            var falseLabel = il.DefineLabel();

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

            il.Emit(OpCodes.Ldnull);
            il.Emit(OpCodes.Ret);

            // read-array header and validate, reader.ReadArrayHeader() != 2) throw;
            il.MarkLabel(falseLabel);

            var rightLabel = il.DefineLabel();
            var writer     = new ArgumentField(il, 1);

            writer.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
            var key = il.DeclareLocal(typeof(int));

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

            // is-sequential don't need else convert key to jump-table value
            if (!IsZeroStartSequential(infos))
            {
                var endKeyMapGet = il.DefineLabel();
                il.EmitLdarg(0);
                il.EmitLdfld(keyToJumpMap);
                il.EmitLdloc(key);
                il.EmitLdloca(key);
                il.EmitCall(keyMapDictionaryTryGetValue);
                il.Emit(OpCodes.Brtrue_S, endKeyMapGet);
                il.EmitLdc_I4(-1);
                il.EmitStloc(key);

                il.MarkLabel(endKeyMapGet);
            }

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

            il.Emit(OpCodes.Ldnull);
            il.EmitStloc(result);
            il.Emit(OpCodes.Ldloc, key);

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

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

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

            foreach (var item in switchLabels)
            {
                il.MarkLabel(item.Label);
                il.EmitLdarg(2);
                il.EmitCall(getFormatterWithVerify.MakeGenericMethod(item.Attr.SubType));
                il.EmitLdarg(1);
                il.EmitLdarg(2);
                il.EmitCall(getDeserialize(item.Attr.SubType));
                if (item.Attr.SubType.GetTypeInfo().IsValueType)
                {
                    il.Emit(OpCodes.Box, item.Attr.SubType);
                }
                il.Emit(OpCodes.Stloc, result);
                il.Emit(OpCodes.Br, loopEnd);
            }

            il.MarkLabel(loopEnd);

            il.Emit(OpCodes.Ldloc, result);
            il.Emit(OpCodes.Ret);
        }
        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);
            }
        }
        // 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);
        }
        static void EmitSerializeValue(
            ILGenerator il,
            TypeInfo type,
            UnionSerializationInfo.EmittableMember member,
            int index,
            ArgumentField argWriter,
            ArgumentField argValue,
            ArgumentField argOptions,
            LocalBuilder localResolver
            )
        {
            Label endLabel = il.DefineLabel();
            Type  t        = member.Type;

            if (IsOptimizeTargetType(t))
            {
                if (!t.GetTypeInfo().IsValueType)
                {
                    // As a nullable type (e.g. byte[] and string) we need to call WriteNil for null values.
                    Label        writeNonNilValueLabel = il.DefineLabel();
                    LocalBuilder memberValue           = il.DeclareLocal(t);
                    argValue.EmitLoad();
                    member.EmitLoadValue(il);
                    il.Emit(OpCodes.Dup);
                    il.EmitStloc(memberValue);
                    il.Emit(OpCodes.Brtrue, writeNonNilValueLabel);
                    argWriter.EmitLoad();
                    il.EmitCall(MessagePackWriterTypeInfo.WriteNil);
                    il.Emit(OpCodes.Br, endLabel);

                    il.MarkLabel(writeNonNilValueLabel);
                    argWriter.EmitLoad();
                    il.EmitLdloc(memberValue);
                }
                else
                {
                    argWriter.EmitLoad();
                    argValue.EmitLoad();
                    member.EmitLoadValue(il);
                }

                if (t == typeof(byte[]))
                {
                    il.EmitCall(ReadOnlySpanFromByteArray);
                    il.EmitCall(MessagePackWriterTypeInfo.WriteBytes);
                }
                else
                {
                    il.EmitCall(typeof(MessagePackWriter).GetRuntimeMethod("Write", new Type[] { t }));
                }
            }
            else
            {
                il.EmitLdloc(localResolver);
                il.Emit(OpCodes.Call, getFormatterWithVerify.MakeGenericMethod(t));

                argWriter.EmitLoad();
                argValue.EmitLoad();
                member.EmitLoadValue(il);
                argOptions.EmitLoad();
                il.EmitCall(getSerialize(t));
            }

            il.MarkLabel(endLabel);
        }
        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++;
                }
            }
        }
        // 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);
        }
Beispiel #11
0
        static void EmitMapMember <TFrom, TTo>(ILGenerator il, MetaMemberPair <TFrom, TTo> pair, ArgumentField argFrom, ArgumentField argResolver, LocalBuilder toLocal, Dictionary <MetaMemberPair <TFrom, TTo>, FieldBuilder> convertFields)
        {
            if (pair.From == null)
            {
                // To and conversion pattern(Func<TFrom, TFromMember>).
                if (toLocal.LocalType.IsValueType)
                {
                    il.EmitLdloca(toLocal);
                }
                else
                {
                    il.EmitLdloc(toLocal);
                }

                // note: if use DynamicMethod, should change field to argument.

                // Func[TFrom, TToMember]
                var convertField = convertFields[pair];
                il.EmitLoadThis();
                il.EmitLdfld(convertField);
                il.Emit(OpCodes.Castclass, typeof(Func <,>).MakeGenericType(typeof(TFrom), pair.To.Type));
                argFrom.EmitLdarg();
                il.EmitCall(EmitInfo.GetFuncInvokeDynamic(typeof(TFrom), pair.To.Type));
                pair.To.EmitStoreValue(il);
            }
            else
            {
                // optimize for primitive to primitive
                if (pair.From.Type == pair.To.Type)
                {
                    if (optimizeInliningType.Contains(pair.To.Type))
                    {
                        if (toLocal.LocalType.IsValueType)
                        {
                            il.EmitLdloca(toLocal);
                        }
                        else
                        {
                            il.EmitLdloc(toLocal);
                        }

                        if (convertFields.TryGetValue(pair, out var convertField))
                        {
                            il.EmitLoadThis();
                            il.EmitLdfld(convertField);
                            il.Emit(OpCodes.Castclass, typeof(Func <,>).MakeGenericType(pair.From.Type, pair.To.Type));
                        }

                        argFrom.EmitLoad();
                        pair.From.EmitLoadValue(il);

                        if (convertField != null)
                        {
                            il.EmitCall(EmitInfo.GetFuncInvokeDynamic(pair.From.Type, pair.To.Type));
                        }

                        pair.To.EmitStoreValue(il);
                        return;
                    }

                    // more aggressive inlining for primitive[]
                    if (pair.To.Type.IsArray)
                    {
                        var elemType      = pair.To.Type.GetElementType();
                        var mapMethodInfo = EmitInfo.GetMemoryCopyMapperMap(elemType);

                        if (mapMethodInfo != null)
                        {
                            il.EmitLdloc(toLocal);

                            if (convertFields.TryGetValue(pair, out var convertField))
                            {
                                il.EmitLoadThis();
                                il.EmitLdfld(convertField);
                                il.Emit(OpCodes.Castclass, typeof(Func <,>).MakeGenericType(pair.From.Type, pair.To.Type));
                            }

                            argFrom.EmitLoad();
                            pair.From.EmitLoadValue(il);
                            il.EmitCall(mapMethodInfo);

                            if (convertField != null)
                            {
                                il.EmitCall(EmitInfo.GetFuncInvokeDynamic(pair.From.Type, pair.To.Type));
                            }

                            pair.To.EmitStoreValue(il);
                            return;
                        }
                    }
                }

                // standard mapping(Mapper.Map)
                {
                    if (toLocal.LocalType.IsValueType)
                    {
                        il.EmitLdloca(toLocal);
                    }
                    else
                    {
                        il.EmitLdloc(toLocal);
                    }

                    if (convertFields.TryGetValue(pair, out var convertField))
                    {
                        il.EmitLoadThis();
                        il.EmitLdfld(convertField);
                        il.Emit(OpCodes.Castclass, typeof(Func <,>).MakeGenericType(pair.From.Type, pair.To.Type));
                    }

                    argResolver.EmitLoad();
                    il.EmitCall(EmitInfo.GetGetMapperWithVerifyDynamic(pair.From.Type, pair.To.Type));
                    argFrom.EmitLoad();
                    pair.From.EmitLoadValue(il);
                    argResolver.EmitLoad();
                    il.EmitCall(EmitInfo.GetMapDynamic(pair.From.Type, pair.To.Type));

                    if (convertField != null)
                    {
                        il.EmitCall(EmitInfo.GetFuncInvokeDynamic(pair.From.Type, pair.To.Type));
                    }

                    pair.To.EmitStoreValue(il);
                }
            }
        }