Ejemplo n.º 1
0
        void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
        {
            using (ctx.StartDebugBlockAuto(this))
            {
                var g = ctx.G;
                using (var value = ctx.GetLocalWithValueForEmitRead(this, valueFrom)) // overwriteList ? null : value
                    using (var result = ctx.Local(ExpectedType))
                    {
                        g.Assign(result, g.ReaderFunc.AppendBytes(value));
                        if (!value.IsNullRef())
                        {
                            g.If(value.AsOperand == null);
                        }
                        {
                            //if (overwriteList || value == null)
                            g.Reader.NoteObject(result);
                        }
                        if (!value.IsNullRef())
                        {
                            g.End();
                        }

                        ctx.LoadValue(result);
                    }
            }
        }
Ejemplo n.º 2
0
        protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
        {
            using (ctx.StartDebugBlockAuto(this, _field.Name))
            {
                using (Compiler.Local loc = ctx.GetLocalWithValueForEmitRead(this, valueFrom))
                    using (Compiler.Local newVal = new Compiler.Local(ctx, _field.FieldType))
                    {
                        Compiler.Local valueForTail;
                        if (Tail.RequiresOldValue)
                        {
                            ctx.LoadAddress(loc, ExpectedType);
                            ctx.LoadValue(_field);
                            if (!Tail.EmitReadReturnsValue)
                            {
                                ctx.StoreValue(newVal);
                                valueForTail = newVal;
                            }
                            else
                            {
                                valueForTail = null; // on stack
                            }
                        }
                        else
                        {
                            valueForTail = null;
                        }

                        Tail.EmitRead(ctx, valueForTail);

                        if (Tail.EmitReadReturnsValue)
                        {
                            ctx.StoreValue(newVal);
                        }

                        ctx.LoadAddress(loc, ExpectedType);
                        ctx.LoadValue(newVal);
                        ctx.StoreValue(_field);

                        if (EmitReadReturnsValue)
                        {
                            ctx.LoadValue(loc);
                        }
                    }
            }
        }
Ejemplo n.º 3
0
        void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
        {
            using (ctx.StartDebugBlockAuto(this))
            {
                using (Compiler.Local value = ctx.GetLocalWithValueForEmitRead(this, valueFrom))
                    using (Compiler.Local converted = new Compiler.Local(ctx, _declaredType)) // declare/re-use local
                        using (Compiler.Local reservedTrap = new Compiler.Local(ctx, typeof(int)))
                        {
                            var g = ctx.G;

                            if (!ExpectedType.IsValueType)
                            {
                                g.Assign(reservedTrap, g.ReaderFunc.ReserveNoteObject_int());
                            }

                            ctx.LoadValue(value);                                                   // load primary onto stack
                            ctx.EmitCall(_toTail);                                                  // static convert op, primary-to-surrogate

                            ctx.StoreValue(converted);                                              // store into surrogate local

                            _rootTail.EmitRead(ctx, _rootTail.RequiresOldValue ? converted : null); // downstream processing against surrogate local

                            if (_rootTail.EmitReadReturnsValue)
                            {
                                ctx.StoreValue(converted);
                            }

                            ctx.LoadValue(converted); // load from surrogate local
                            ctx.EmitCall(_fromTail);  // static convert op, surrogate-to-primary
                            ctx.StoreValue(value);    // store back into primary

                            if (!ExpectedType.IsValueType)
                            {
                                g.Reader.NoteReservedTrappedObject(reservedTrap, value);
                            }

                            if (EmitReadReturnsValue)
                            {
                                ctx.LoadValue(value);
                            }
                        }
            }
        }
        public void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
        {
            using (ctx.StartDebugBlockAuto(this))
            {
                var g = ctx.G;
                using (var value = ctx.GetLocalWithValueForEmitRead(this, valueFrom))
                {
                    _subTypeHelpers.EmitTryRead(
                        g,
                        value,
                        _model[_typeKey],
                        r =>
                    {
                        using (ctx.StartDebugBlockAuto(this, "returnGen"))
                        {
                            if (r == null)
                            {
                                g.If(value.AsOperand == null);
                                {
                                    g.ThrowProtoException(CantCreateInstanceMessage);
                                }
                                g.End();
                                g.Reader.NoteObject(value);
                            }
                            else
                            {
                                r.Serializer.EmitCreateInstance(ctx);
                                ctx.StoreValue(value);
                            }
                            g.Reader.NoteLateReference(ctx.MapMetaKeyToCompiledKey(_baseTypeKey), value);
                        }
                    });

                    if (EmitReadReturnsValue)
                    {
                        ctx.LoadValue(value);
                    }
                }
            }
        }
Ejemplo n.º 5
0
        public void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
        {
            using (ctx.StartDebugBlockAuto(this))
            {
                var g = ctx.G;

                g.Reader.ExpectRoot();
                if (_protoCompatibility)
                {
                    _serializer.EmitRead(ctx, _serializer.RequiresOldValue ? valueFrom : null);
                    return;
                }

                using (var rootToken = ctx.Local(typeof(SubItemToken)))
                    using (var typeKey = ctx.Local(typeof(int)))
                        using (var obj = ctx.Local(typeof(object)))
                            using (var formatVersion = ctx.Local(typeof(int)))
                                using (var expectedRefKey = ctx.Local(typeof(int)))
                                    using (var actualRefKey = ctx.Local(typeof(int)))
                                        using (var value = ctx.GetLocalWithValueForEmitRead(this, valueFrom))
                                        {
                                            g.Assign(rootToken, g.ReaderFunc.StartSubItem());
                                            g.Assign(formatVersion, g.ReaderFunc.ReadFieldHeader_int());
                                            g.If(formatVersion.AsOperand != CurrentFormatVersion);
                                            {
                                                g.ThrowProtoException("Wrong format version, required " + CurrentFormatVersion + " but actual " + formatVersion.AsOperand);
                                            }
                                            g.End();
                                            _serializer.EmitRead(ctx, _serializer.RequiresOldValue ? value : null);
                                            if (_serializer.EmitReadReturnsValue)
                                            {
                                                g.Assign(value, g.GetStackValueOperand(ExpectedType));
                                            }

                                            g.While(g.StaticFactory.Invoke(typeof(ProtoReader), nameof(ProtoReader.TryGetNextLateReference), typeKey, obj, expectedRefKey, g.ArgReaderWriter()));
                                            {
                                                g.DoWhile();
                                                {
                                                    g.Assign(actualRefKey, g.ReaderFunc.ReadFieldHeader_int() - 1);
                                                    g.If(actualRefKey.AsOperand != expectedRefKey.AsOperand);
                                                    {
                                                        g.If(actualRefKey.AsOperand <= -1);
                                                        {
                                                            g.ThrowProtoException("Expected field for late reference");
                                                        }
                                                        g.End();
                                                        g.If(actualRefKey.AsOperand > expectedRefKey.AsOperand);
                                                        {
                                                            g.ThrowProtoException("Mismatched order of late reference objects");
                                                        }
                                                        g.End();
                                                        g.Reader.SkipField();
                                                    }
                                                    g.End();
                                                }
                                                g.EndDoWhile(actualRefKey.AsOperand < expectedRefKey.AsOperand);
                                                g.If(!g.StaticFactory.InvokeReferenceEquals(g.ReaderFunc.ReadObject(obj, typeKey), obj));
                                                {
                                                    g.ThrowProtoException("Late reference changed during deserializing");
                                                }
                                                g.End();
                                            }
                                            g.End();
                                            g.Reader.EndSubItem(rootToken);

                                            if (EmitReadReturnsValue)
                                            {
                                                ctx.LoadValue(value);
                                            }
                                        }
            }
        }
Ejemplo n.º 6
0
        protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
        {
            using (ctx.StartDebugBlockAuto(this, _property.Name))
            {
                var  g = ctx.G;
                bool canSet;
                SanityCheck(ctx.Model, _property, Tail, out canSet, ctx.NonPublic, ctx.AllowInternal(_property), true);

                using (Compiler.Local loc = ctx.GetLocalWithValueForEmitRead(this, valueFrom))
                    using (Compiler.Local oldVal = canSet ? new Compiler.Local(ctx, _property.PropertyType) : null)
                        using (Compiler.Local newVal = new Compiler.Local(ctx, _property.PropertyType))
                        {
                            Compiler.Local valueForTail = null;
                            if (Tail.RequiresOldValue)
                            {
                                ctx.LoadAddress(loc, ExpectedType);
                                ctx.LoadValue(_property);
                                if (!oldVal.IsNullRef())
                                {
                                    if (Tail.RequiresOldValue) // we need it later
                                    {
                                        ctx.CopyValue();
                                    }
                                    ctx.StoreValue(oldVal);
                                }
                            }

                            if (Tail.RequiresOldValue) // !here! <-- we need value again
                            {
                                if (!Tail.EmitReadReturnsValue)
                                {
                                    ctx.StoreValue(newVal);
                                    valueForTail = newVal;
                                } // otherwise valueForTail = null (leave value on stack)
                            }

                            Tail.EmitRead(ctx, valueForTail);

                            // otherwise newVal was passed to EmitRead so already has necessary data
                            if (Tail.EmitReadReturnsValue)
                            {
                                ctx.StoreValue(newVal);
                            }

                            // check-condition:
                            //(!Tail.RequiresOldValue // always set where can't check oldVal
                            //                        // and if it's value type or nullable with changed null/not null or ref
                            //    || (Helpers.IsValueType(property.PropertyType) && oldVal != null && newVal != null)
                            //    || !ReferenceEquals(oldVal, newVal)
                            //   ))
                            if (canSet)
                            {
                                bool check = Tail.RequiresOldValue;
                                if (check)
                                {
                                    var condition = !g.StaticFactory.InvokeReferenceEquals(oldVal, newVal);

                                    if (Helpers.IsValueType(_property.PropertyType))
                                    {
                                        condition = (oldVal.AsOperand != null && newVal.AsOperand != null) || condition;
                                    }

                                    g.If(condition);
                                }

                                ctx.LoadAddress(loc, ExpectedType);
                                ctx.LoadValue(newVal);
                                if (_shadowSetter == null)
                                {
                                    ctx.StoreValue(_property);
                                }
                                else
                                {
                                    ctx.EmitCall(_shadowSetter);
                                }

                                if (check)
                                {
                                    g.End();
                                }
                            }

                            if (EmitReadReturnsValue)
                            {
                                ctx.LoadValue(loc);
                            }
                        }
            }
        }
Ejemplo n.º 7
0
        protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
        {
            using (ctx.StartDebugBlockAuto(this))
            {
                Type voidType = ctx.MapType(typeof(void));
                using (Compiler.Local value = ctx.GetLocalWithValueForEmitRead(this, valueFrom))
                    using (Compiler.Local builderInstance = new Compiler.Local(ctx, _builderFactory.ReturnType))
                        using (Compiler.Local trappedKey = new Compiler.Local(ctx, typeof(int)))
                        {
                            ctx.G.Assign(trappedKey, ctx.G.ReaderFunc.ReserveNoteObject_int());
                            ctx.EmitCall(_builderFactory);
                            ctx.StoreValue(builderInstance);

                            if (AppendToCollection)
                            {
                                Compiler.CodeLabel done = ctx.DefineLabel();
                                if (!ExpectedType.IsValueType)
                                {
                                    ctx.LoadValue(value);
                                    ctx.BranchIfFalse(done, false); // old value null; nothing to add
                                }
                                PropertyInfo prop = Helpers.GetProperty(ExpectedType, "Length", false) ?? Helpers.GetProperty(ExpectedType, "Count", false);
#if !NO_GENERICS
                                if (prop == null)
                                {
                                    prop = Helpers.GetProperty(ResolveIReadOnlyCollection(ExpectedType, Tail.ExpectedType), "Count", false);
                                }
#endif
                                ctx.LoadAddress(value, value.Type);
                                ctx.EmitCall(Helpers.GetGetMethod(prop, false, false));
                                ctx.BranchIfFalse(done, false); // old list is empty; nothing to add

                                if (_addRange != null)
                                {
                                    ctx.LoadValue(builderInstance);
                                    ctx.LoadValue(value);
                                    ctx.EmitCall(_addRange);
                                    if (_addRange.ReturnType != null && _add.ReturnType != voidType)
                                    {
                                        ctx.DiscardValue();
                                    }
                                }
                                else
                                {
                                    // loop and call Add repeatedly
                                    MethodInfo moveNext, current, getEnumerator = GetEnumeratorInfo(ctx.Model, out moveNext, out current);
                                    Helpers.DebugAssert(moveNext != null);
                                    Helpers.DebugAssert(current != null);
                                    Helpers.DebugAssert(getEnumerator != null);

                                    Type enumeratorType = getEnumerator.ReturnType;
                                    using (Compiler.Local iter = new Compiler.Local(ctx, enumeratorType))
                                    {
                                        ctx.LoadAddress(value, ExpectedType);
                                        ctx.EmitCall(getEnumerator);
                                        ctx.StoreValue(iter);
                                        using (ctx.Using(iter))
                                        {
                                            Compiler.CodeLabel body = ctx.DefineLabel(), next = ctx.DefineLabel();
                                            ctx.Branch(next, false);

                                            ctx.MarkLabel(body);
                                            ctx.LoadAddress(builderInstance, builderInstance.Type);
                                            ctx.LoadAddress(iter, enumeratorType);
                                            ctx.EmitCall(current);
                                            ctx.EmitCall(_add);
                                            if (_add.ReturnType != null && _add.ReturnType != voidType)
                                            {
                                                ctx.DiscardValue();
                                            }

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


                                ctx.MarkLabel(done);
                            }

                            ListHelpers.EmitRead(
                                ctx.G,
                                null,
                                null,
                                o =>
                            {
                                using (ctx.StartDebugBlockAuto(this, "add"))
                                {
                                    ctx.LoadAddress(builderInstance, builderInstance.Type);
                                    ctx.LoadValue(o);
                                    ctx.EmitCall(_add);
                                    if (_add.ReturnType != null && _add.ReturnType != voidType)
                                    {
                                        ctx.DiscardValue();
                                    }
                                }
                            });

                            ctx.LoadAddress(builderInstance, builderInstance.Type);
                            ctx.EmitCall(_finish);
                            if (ExpectedType != _finish.ReturnType)
                            {
                                ctx.Cast(ExpectedType);
                            }
                            ctx.StoreValue(value);
                            ctx.G.Reader.NoteReservedTrappedObject(trappedKey, value);

                            if (EmitReadReturnsValue)
                            {
                                ctx.LoadValue(value);
                            }
                        }
            }
        }
Ejemplo n.º 8
0
        protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
        {
            using (ctx.StartDebugBlockAuto(this))
            {
                using (Compiler.Local oldValue = ctx.GetLocalWithValueForEmitRead(this, valueFrom))
                {
                    Compiler.Local tempLocal = null;
                    Compiler.Local valueForTail;
                    Debug.Assert(Tail.RequiresOldValue || Tail.EmitReadReturnsValue);
                    if (Tail.RequiresOldValue)
                    {
                        if (_expectedType.IsValueType)
                        {
                            tempLocal = ctx.Local(Tail.ExpectedType);
                            ctx.LoadAddress(oldValue, _expectedType);
                            ctx.EmitCall(_expectedType.GetMethod("GetValueOrDefault", Helpers.EmptyTypes));
                            ctx.StoreValue(tempLocal);
                            valueForTail = tempLocal;
                        }
                        else
                        {
                            valueForTail = oldValue;
                        }
                    }
                    else
                    {
                        valueForTail = null;
                    }

                    // valueForTail contains:
                    // null: when not required old value
                    // oldValue local: when reference type
                    // tempLocal: when nullable value type

                    Tail.EmitRead(ctx, valueForTail);

                    if (_expectedType.IsValueType)
                    {
                        if (!Tail.EmitReadReturnsValue)
                        {
                            ctx.LoadValue(tempLocal);
                        }
                        // note we demanded always returns a value
                        ctx.EmitCtor(_expectedType, Tail.ExpectedType); // re-nullable<T> it
                    }

                    if (Tail.EmitReadReturnsValue || _expectedType.IsValueType)
                    {
                        ctx.StoreValue(oldValue);
                    }

                    if (EmitReadReturnsValue)
                    {
                        ctx.LoadValue(oldValue);
                    }

                    if (!tempLocal.IsNullRef())
                    {
                        tempLocal.Dispose();
                    }
                }
            }
        }
Ejemplo n.º 9
0
        public void EmitRead(Compiler.CompilerContext ctx, Compiler.Local incoming)
        {
            using (ctx.StartDebugBlockAuto(this))
            {
                var g = ctx.G;

                using (Compiler.Local objValue = ctx.GetLocalWithValueForEmitRead(this, incoming))
                    using (Compiler.Local reservedTrap = new Local(ctx, ctx.MapType(typeof(int))))
                        using (Compiler.Local token = new Local(ctx, ctx.MapType(typeof(SubItemToken))))
                            using (Compiler.Local refLocalToNoteObject = new Local(ctx, ctx.MapType(typeof(object))))
                            {
                                ctx.EmitCallReserveNoteObject();
                                ctx.StoreValue(reservedTrap);

                                Compiler.Local[] locals = new Compiler.Local[_members.Length];
                                try
                                {
                                    g.Assign(token, g.ReaderFunc.StartSubItem());

                                    for (int i = 0; i < locals.Length; i++)
                                    {
                                        Type type  = GetMemberType(i);
                                        bool store = true;
                                        locals[i] = new Compiler.Local(ctx, type);
                                        if (!ExpectedType.IsValueType)
                                        {
                                            // value-types always read the old value
                                            if (type.IsValueType)
                                            {
                                                switch (Helpers.GetTypeCode(type))
                                                {
                                                case ProtoTypeCode.Boolean:
                                                case ProtoTypeCode.Byte:
                                                case ProtoTypeCode.Int16:
                                                case ProtoTypeCode.Int32:
                                                case ProtoTypeCode.SByte:
                                                case ProtoTypeCode.UInt16:
                                                case ProtoTypeCode.UInt32:
                                                    ctx.LoadValue(0);
                                                    break;

                                                case ProtoTypeCode.Int64:
                                                case ProtoTypeCode.UInt64:
                                                    ctx.LoadValue(0L);
                                                    break;

                                                case ProtoTypeCode.Single:
                                                    ctx.LoadValue(0.0F);
                                                    break;

                                                case ProtoTypeCode.Double:
                                                    ctx.LoadValue(0.0D);
                                                    break;

                                                case ProtoTypeCode.Decimal:
                                                    ctx.LoadValue(0M);
                                                    break;

                                                case ProtoTypeCode.Guid:
                                                    ctx.LoadValue(Guid.Empty);
                                                    break;

                                                default:
                                                    ctx.LoadAddress(locals[i], type);
                                                    ctx.EmitCtor(type);
                                                    store = false;
                                                    break;
                                                }
                                            }
                                            else
                                            {
                                                ctx.LoadNullRef();
                                            }
                                            if (store)
                                            {
                                                ctx.StoreValue(locals[i]);
                                            }
                                        }
                                    }

                                    Compiler.CodeLabel skipOld = ExpectedType.IsValueType
                                                         ? new Compiler.CodeLabel()
                                                         : ctx.DefineLabel();
                                    if (!ExpectedType.IsValueType)
                                    {
                                        ctx.LoadAddress(objValue, ExpectedType);
                                        ctx.BranchIfFalse(skipOld, false);
                                    }
                                    for (int i = 0; i < _members.Length; i++)
                                    {
                                        ctx.LoadAddress(objValue, ExpectedType);
                                        switch (_members[i].Member.MemberType)
                                        {
                                        case MemberTypes.Field:
                                            ctx.LoadValue((FieldInfo)_members[i].Member);
                                            break;

                                        case MemberTypes.Property:
                                            ctx.LoadValue((PropertyInfo)_members[i].Member);
                                            break;
                                        }
                                        ctx.StoreValue(locals[i]);
                                    }

                                    if (!ExpectedType.IsValueType)
                                    {
                                        ctx.MarkLabel(skipOld);
                                    }

                                    using (Compiler.Local fieldNumber = new Compiler.Local(ctx, ctx.MapType(typeof(int))))
                                    {
                                        Compiler.CodeLabel @continue     = ctx.DefineLabel(),
                                                           processField  = ctx.DefineLabel(),
                                                           notRecognised = ctx.DefineLabel();
                                        ctx.Branch(@continue, false);

                                        Compiler.CodeLabel[] handlers = new Compiler.CodeLabel[_members.Length];
                                        for (int i = 0; i < _members.Length; i++)
                                        {
                                            handlers[i] = ctx.DefineLabel();
                                        }

                                        ctx.MarkLabel(processField);

                                        ctx.LoadValue(fieldNumber);
                                        ctx.LoadValue(1);
                                        ctx.Subtract(); // jump-table is zero-based
                                        ctx.Switch(handlers);

                                        // and the default:
                                        ctx.Branch(notRecognised, false);
                                        for (int i = 0; i < handlers.Length; i++)
                                        {
                                            ctx.MarkLabel(handlers[i]);
                                            IProtoSerializer tail           = _tails[i];
                                            Compiler.Local   oldValIfNeeded = tail.RequiresOldValue ? locals[i] : null;
                                            ctx.ReadNullCheckedTail(locals[i].Type, tail, oldValIfNeeded);
                                            if (tail.EmitReadReturnsValue)
                                            {
                                                if (locals[i].Type.IsValueType)
                                                {
                                                    ctx.StoreValue(locals[i]);
                                                }
                                                else
                                                {
                                                    Compiler.CodeLabel hasValue = ctx.DefineLabel(), allDone = ctx.DefineLabel();

                                                    ctx.CopyValue();
                                                    ctx.BranchIfTrue(hasValue, true); // interpret null as "don't assign"
                                                    ctx.DiscardValue();
                                                    ctx.Branch(allDone, true);
                                                    ctx.MarkLabel(hasValue);
                                                    ctx.StoreValue(locals[i]);
                                                    ctx.MarkLabel(allDone);
                                                }
                                            }
                                            ctx.Branch(@continue, false);
                                        }

                                        ctx.MarkLabel(notRecognised);
                                        ctx.LoadReaderWriter();
                                        ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("SkipField"));

                                        ctx.MarkLabel(@continue);
                                        ctx.EmitBasicRead("ReadFieldHeader", ctx.MapType(typeof(int)));
                                        ctx.CopyValue();
                                        ctx.StoreValue(fieldNumber);
                                        ctx.LoadValue(0);
                                        ctx.BranchIfGreater(processField, false);
                                    }

                                    g.Reader.EndSubItem(token);

                                    for (int i = 0; i < locals.Length; i++)
                                    {
                                        ctx.LoadValue(locals[i]);
                                    }
                                    ctx.EmitCtor(_ctor);
                                    ctx.StoreValue(objValue);

                                    ctx.LoadValue(objValue);
                                    ctx.CastToObject(ctx.MapType(_ctor.DeclaringType));
                                    ctx.StoreValue(refLocalToNoteObject);

                                    ctx.LoadValue(reservedTrap);
                                    ctx.LoadAddress(refLocalToNoteObject, refLocalToNoteObject.Type);
                                    ctx.EmitCallNoteReservedTrappedObject();

                                    if (EmitReadReturnsValue)
                                    {
                                        ctx.LoadValue(objValue);
                                    }
                                }
                                finally
                                {
                                    for (int i = 0; i < locals.Length; i++)
                                    {
                                        if (!locals[i].IsNullRef())
                                        {
                                            locals[i].Dispose(); // release for re-use
                                        }
                                    }
                                }
                            }
            }
        }
Ejemplo n.º 10
0
        void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
        {
            using (ctx.StartDebugBlockAuto(this))
            {
                var g = ctx.G;
                Helpers.DebugAssert(!valueFrom.IsNullRef());

                using (Compiler.Local loc = ctx.GetLocalWithValueForEmitRead(this, valueFrom))
                    using (Compiler.Local token = ctx.Local(typeof(SubItemToken)))
                        using (Compiler.Local fieldNumber = new Compiler.Local(ctx, ctx.MapType(typeof(int))))
                        {
                            g.Assign(token, g.ReaderFunc.StartSubItem());
                            // pre-callbacks
                            if (HasCallbacks(TypeModel.CallbackType.BeforeDeserialize))
                            {
                                if (ExpectedType.IsValueType)
                                {
                                    EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.BeforeDeserialize);
                                }
                                else
                                { // could be null
                                    Compiler.CodeLabel callbacksDone = ctx.DefineLabel();
                                    ctx.LoadValue(loc);
                                    ctx.BranchIfFalse(callbacksDone, false);
                                    EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.BeforeDeserialize);
                                    ctx.MarkLabel(callbacksDone);
                                }
                            }

                            Compiler.CodeLabel @continue = ctx.DefineLabel(), processField = ctx.DefineLabel();
                            ctx.Branch(@continue, false);

                            ctx.MarkLabel(processField);
                            foreach (BasicList.Group group in BasicList.GetContiguousGroups(_fieldNumbers, _serializers))
                            {
                                Compiler.CodeLabel tryNextField = ctx.DefineLabel();
                                int groupItemCount = group.Items.Count;
                                if (groupItemCount == 1)
                                {
                                    // discreet group; use an equality test
                                    ctx.LoadValue(fieldNumber);
                                    ctx.LoadValue(group.First);
                                    Compiler.CodeLabel processThisField = ctx.DefineLabel();
                                    ctx.BranchIfEqual(processThisField, true);
                                    ctx.Branch(tryNextField, false);
                                    WriteFieldHandler(ctx, loc, processThisField, @continue, (IProtoSerializer)group.Items[0]);
                                }
                                else
                                { // implement as a jump-table-based switch
                                    ctx.LoadValue(fieldNumber);
                                    ctx.LoadValue(group.First);
                                    ctx.Subtract(); // jump-tables are zero-based
                                    Compiler.CodeLabel[] jmp = new Compiler.CodeLabel[groupItemCount];
                                    for (int i = 0; i < groupItemCount; i++)
                                    {
                                        jmp[i] = ctx.DefineLabel();
                                    }
                                    ctx.Switch(jmp);
                                    // write the default...
                                    ctx.Branch(tryNextField, false);
                                    for (int i = 0; i < groupItemCount; i++)
                                    {
                                        WriteFieldHandler(ctx, loc, jmp[i], @continue, (IProtoSerializer)group.Items[i]);
                                    }
                                }
                                ctx.MarkLabel(tryNextField);
                            }

                            EmitCreateIfNull(ctx, loc);
                            ctx.LoadReaderWriter();
                            if (_isExtensible)
                            {
                                ctx.LoadValue(loc);
                                ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("AppendExtensionData"));
                            }
                            else
                            {
                                ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("SkipField"));
                            }

                            ctx.MarkLabel(@continue);
                            ctx.EmitBasicRead("ReadFieldHeader", ctx.MapType(typeof(int)));
                            ctx.CopyValue();
                            ctx.StoreValue(fieldNumber);
                            ctx.LoadValue(0);
                            ctx.BranchIfGreater(processField, false);

                            EmitCreateIfNull(ctx, loc);
                            // post-callbacks
                            EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.AfterDeserialize);

                            g.Reader.EndSubItem(token);

                            if (EmitReadReturnsValue)
                            {
                                ctx.LoadValue(loc);
                            }
                        }
            }
        }