public TupleSerializer(RuntimeTypeModel model, ConstructorInfo ctor, MemberInfo[] members)
        {
            this.ctor    = ctor ?? throw new ArgumentNullException(nameof(ctor));
            this.members = members ?? throw new ArgumentNullException(nameof(members));
            this.tails   = new IProtoSerializer[members.Length];

            ParameterInfo[] parameters = ctor.GetParameters();
            for (int i = 0; i < members.Length; i++)
            {
                WireType wireType;
                Type     finalType = parameters[i].ParameterType;

                Type itemType = null, defaultType = null;

                MetaType.ResolveListTypes(model, finalType, ref itemType, ref defaultType);
                Type tmp = itemType == null ? finalType : itemType;

                bool asReference = false;
                int  typeIndex   = model.FindOrAddAuto(tmp, false, true, false);
                if (typeIndex >= 0)
                {
                    asReference = model[tmp].AsReferenceDefault;
                }
                IProtoSerializer tail = ValueMember.TryGetCoreSerializer(model, DataFormat.Default, tmp, out wireType, asReference, false, false, true), serializer;
                if (tail == null)
                {
                    throw new InvalidOperationException("No serializer defined for type: " + tmp.FullName);
                }

                tail = new TagDecorator(i + 1, wireType, false, tail);
                if (itemType == null)
                {
                    serializer = tail;
                }
                else
                {
                    if (finalType.IsArray)
                    {
                        serializer = new ArrayDecorator(model, tail, i + 1, false, wireType, finalType, false, false);
                    }
                    else
                    {
                        serializer = ListDecorator.Create(model, finalType, defaultType, tail, i + 1, false, wireType, true, false, false);
                    }
                }
                tails[i] = serializer;
            }
        }
        public ArrayDecorator(TypeModel model, IProtoSerializer tail, int fieldNumber, bool writePacked, WireType packedWireType, Type arrayType, bool overwriteList, bool supportNull)
            : base(tail)
        {
            Helpers.DebugAssert(arrayType != null, "arrayType should be non-null");
            Helpers.DebugAssert(arrayType.IsArray && arrayType.GetArrayRank() == 1, "should be single-dimension array; " + arrayType.FullName);
            this.itemType = arrayType.GetElementType();
            Type underlyingItemType = supportNull ? itemType : (Helpers.GetUnderlyingType(itemType) ?? itemType);

            Helpers.DebugAssert(underlyingItemType == Tail.ExpectedType ||
                                (Tail.ExpectedType == model.MapType(typeof(object)) && !Helpers.IsValueType(underlyingItemType)), "invalid tail");
            Helpers.DebugAssert(Tail.ExpectedType != model.MapType(typeof(byte)), "Should have used BlobSerializer");
            if ((writePacked || packedWireType != WireType.None) && fieldNumber <= 0)
            {
                throw new ArgumentOutOfRangeException("fieldNumber");
            }
            if (!ListDecorator.CanPack(packedWireType))
            {
                if (writePacked)
                {
                    throw new InvalidOperationException("Only simple data-types can use packed encoding");
                }
                packedWireType = WireType.None;
            }
            this.fieldNumber    = fieldNumber;
            this.packedWireType = packedWireType;
            if (writePacked)
            {
                options |= OPTIONS_WritePacked;
            }
            if (overwriteList)
            {
                options |= OPTIONS_OverwriteList;
            }
            if (supportNull)
            {
                options |= OPTIONS_SupportNull;
            }
            this.arrayType = arrayType;
        }
        protected override void EmitRead(ProtoBuf.Compiler.CompilerContext ctx, ProtoBuf.Compiler.Local valueFrom)
        {
            Type listType;

            listType = ctx.MapType(typeof(System.Collections.Generic.List <>)).MakeGenericType(itemType);
            Type expected = ExpectedType;

            using (Compiler.Local oldArr = AppendToCollection ? ctx.GetLocalWithValue(expected, valueFrom) : null)
                using (Compiler.Local newArr = new Compiler.Local(ctx, expected))
                    using (Compiler.Local list = new Compiler.Local(ctx, listType))
                    {
                        ctx.EmitCtor(listType);
                        ctx.StoreValue(list);
                        ListDecorator.EmitReadList(ctx, list, Tail, listType.GetMethod("Add"), packedWireType, false);

                        // leave this "using" here, as it can share the "FieldNumber" local with EmitReadList
                        using (Compiler.Local oldLen = AppendToCollection ? new ProtoBuf.Compiler.Local(ctx, ctx.MapType(typeof(int))) : null)
                        {
                            Type[] copyToArrayInt32Args = new Type[] { ctx.MapType(typeof(Array)), ctx.MapType(typeof(int)) };

                            if (AppendToCollection)
                            {
                                ctx.LoadLength(oldArr, true);
                                ctx.CopyValue();
                                ctx.StoreValue(oldLen);

                                ctx.LoadAddress(list, listType);
                                ctx.LoadValue(listType.GetProperty("Count"));
                                ctx.Add();
                                ctx.CreateArray(itemType, null); // length is on the stack
                                ctx.StoreValue(newArr);

                                ctx.LoadValue(oldLen);
                                Compiler.CodeLabel nothingToCopy = ctx.DefineLabel();
                                ctx.BranchIfFalse(nothingToCopy, true);
                                ctx.LoadValue(oldArr);
                                ctx.LoadValue(newArr);
                                ctx.LoadValue(0); // index in target

                                ctx.EmitCall(expected.GetMethod("CopyTo", copyToArrayInt32Args));
                                ctx.MarkLabel(nothingToCopy);

                                ctx.LoadValue(list);
                                ctx.LoadValue(newArr);
                                ctx.LoadValue(oldLen);
                            }
                            else
                            {
                                ctx.LoadAddress(list, listType);
                                ctx.LoadValue(listType.GetProperty("Count"));
                                ctx.CreateArray(itemType, null);
                                ctx.StoreValue(newArr);

                                ctx.LoadAddress(list, listType);
                                ctx.LoadValue(newArr);
                                ctx.LoadValue(0);
                            }

                            copyToArrayInt32Args[0] = expected; // // prefer: CopyTo(T[], int)
                            MethodInfo copyTo = listType.GetMethod("CopyTo", copyToArrayInt32Args);
                            if (copyTo == null)
                            { // fallback: CopyTo(Array, int)
                                copyToArrayInt32Args[1] = ctx.MapType(typeof(Array));
                                copyTo = listType.GetMethod("CopyTo", copyToArrayInt32Args);
                            }
                            ctx.EmitCall(copyTo);
                        }
                        ctx.LoadValue(newArr);
                    }
        }
Exemple #4
0
        protected override void EmitWrite(CompilerContext ctx, Local valueFrom)
        {
            Type       itemType = typeof(KeyValuePair <TKey, TValue>);
            MethodInfo moveNext, current, getEnumerator = ListDecorator.GetEnumeratorInfo(ctx.Model,
                                                                                          ExpectedType, itemType, out moveNext, out current);
            Type enumeratorType = getEnumerator.ReturnType;

            MethodInfo key    = itemType.GetProperty(nameof(KeyValuePair <TKey, TValue> .Key)).GetGetMethod(),
                       @value = itemType.GetProperty(nameof(KeyValuePair <TKey, TValue> .Value)).GetGetMethod();

            using (Compiler.Local list = ctx.GetLocalWithValue(ExpectedType, valueFrom))
                using (Compiler.Local iter = new Compiler.Local(ctx, enumeratorType))
                    using (Compiler.Local token = new Compiler.Local(ctx, typeof(SubItemToken)))
                        using (Compiler.Local kvp = new Compiler.Local(ctx, itemType))
                        {
                            ctx.LoadAddress(list, ExpectedType);
                            ctx.EmitCall(getEnumerator, ExpectedType);
                            ctx.StoreValue(iter);
                            using (ctx.Using(iter))
                            {
                                Compiler.CodeLabel body = ctx.DefineLabel(), next = ctx.DefineLabel();
                                ctx.Branch(next, false);

                                ctx.MarkLabel(body);

                                ctx.LoadAddress(iter, enumeratorType);
                                ctx.EmitCall(current, enumeratorType);

                                if (itemType != ctx.MapType(typeof(object)) && current.ReturnType == ctx.MapType(typeof(object)))
                                {
                                    ctx.CastFromObject(itemType);
                                }
                                ctx.StoreValue(kvp);

                                ctx.LoadValue(fieldNumber);
                                ctx.LoadValue((int)wireType);
                                ctx.LoadReaderWriter();
                                ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("WriteFieldHeader"));

                                ctx.LoadNullRef();
                                ctx.LoadReaderWriter();
                                ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("StartSubItem"));
                                ctx.StoreValue(token);

                                ctx.LoadAddress(kvp, itemType);
                                ctx.EmitCall(key, itemType);
                                ctx.WriteNullCheckedTail(typeof(TKey), keyTail, null);

                                ctx.LoadAddress(kvp, itemType);
                                ctx.EmitCall(value, itemType);
                                ctx.WriteNullCheckedTail(typeof(TValue), Tail, null);

                                ctx.LoadValue(token);
                                ctx.LoadReaderWriter();
                                ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("EndSubItem"));

                                ctx.MarkLabel(@next);
                                ctx.LoadAddress(iter, enumeratorType);
                                ctx.EmitCall(moveNext, enumeratorType);
                                ctx.BranchIfTrue(body, false);
                            }
                        }
        }