Example #1
0
        private void WriteFieldHandler(
            Compiler.CompilerContext ctx, Compiler.Local loc,
            Compiler.CodeLabel handler, Compiler.CodeLabel @continue, IProtoSerializer serializer)
        {
            ctx.MarkLabel(handler);
            Type serType = serializer.ExpectedType;

            if (serType == ExpectedType)
            {
                // emit create if null
                Helpers.DebugAssert(!loc.IsNullRef());
                if (!ExpectedType.IsValueType && CanCreateInstance)
                {
                    Compiler.CodeLabel afterIf   = ctx.DefineLabel();
                    Compiler.CodeLabel ifContent = ctx.DefineLabel();

                    // if != null && of correct type
                    ctx.LoadValue(loc);
                    ctx.BranchIfFalse(ifContent, false);
                    ctx.LoadValue(loc);
                    ctx.TryCast(ExpectedType);
                    ctx.BranchIfFalse(ifContent, false);
                    ctx.Branch(afterIf, false);
                    {
                        ctx.MarkLabel(ifContent);

                        ((IProtoTypeSerializer)this).EmitCreateInstance(ctx);

                        if (_callbacks != null)
                        {
                            EmitInvokeCallback(ctx, _callbacks.BeforeDeserialize, true, null, ExpectedType);
                        }
                        ctx.StoreValue(loc);
                    }
                    ctx.MarkLabel(afterIf);
                }

                serializer.EmitRead(ctx, loc);
            }
            else
            {
                ctx.LoadValue(loc);
                if (ExpectedType.IsValueType || !serializer.EmitReadReturnsValue)
                {
                    ctx.Cast(serType);
                }
                else
                {
                    ctx.TryCast(serType); // default value can be another inheritance branch
                }
                serializer.EmitRead(ctx, null);
            }

            if (serializer.EmitReadReturnsValue)
            {   // update the variable
                ctx.StoreValue(loc);
            }
            ctx.Branch(@continue, false); // "continue"
        }
Example #2
0
 private void EmitCallbackIfNeeded(Compiler.CompilerContext ctx, Compiler.Local valueFrom, TypeModel.CallbackType callbackType)
 {
     Helpers.DebugAssert(!valueFrom.IsNullRef());
     if (_isRootType && ((IProtoTypeSerializer)this).HasCallbacks(callbackType))
     {
         ((IProtoTypeSerializer)this).EmitCallback(ctx, valueFrom, callbackType);
     }
 }
Example #3
0
        private void EmitCreateIfNull(Compiler.CompilerContext ctx, Compiler.Local storage)
        {
            Helpers.DebugAssert(!storage.IsNullRef());
            if (!ExpectedType.IsValueType && CanCreateInstance)
            {
                Compiler.CodeLabel afterNullCheck = ctx.DefineLabel();
                ctx.LoadValue(storage);
                ctx.BranchIfTrue(afterNullCheck, false);

                ((IProtoTypeSerializer)this).EmitCreateInstance(ctx);

                if (_callbacks != null)
                {
                    EmitInvokeCallback(ctx, _callbacks.BeforeDeserialize, true, null, ExpectedType);
                }
                ctx.StoreValue(storage);
                ctx.MarkLabel(afterNullCheck);
            }
        }
Example #4
0
        protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
        {
            using (ctx.StartDebugBlockAuto(this))
            {
                Compiler.CodeLabel done     = ctx.DefineLabel();
                Compiler.CodeLabel onCancel = ctx.DefineLabel();
                if (valueFrom.IsNullRef())
                {
                    ctx.CopyValue(); // on the stack
                    Compiler.CodeLabel needToPop = ctx.DefineLabel();

                    EmitBranchIfDefaultValue(ctx, needToPop);
                    // if != defaultValue
                    {
                        Tail.EmitWrite(ctx, null);
                        ctx.Branch(done, true);
                    }
                    // else
                    {
                        ctx.MarkLabel(needToPop);
                        ctx.DiscardValue();
                        // onCancel
                    }
                }
                else
                {
                    ctx.LoadValue(valueFrom); // variable/parameter

                    EmitBranchIfDefaultValue(ctx, onCancel);
                    // if != defaultValue
                    {
                        Tail.EmitWrite(ctx, valueFrom);
                        ctx.Branch(done, true);
                    }
                    // else
                    // onCancel
                }
                ctx.MarkLabel(onCancel);
                ctx.LoadReaderWriter();
                ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod(nameof(ProtoWriter.WriteFieldHeaderCancelBegin)));
                ctx.MarkLabel(done);
            }
        }
Example #5
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);
                            }
                        }
            }
        }
Example #6
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();
                    }
                }
            }
        }
Example #7
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);
                            }
                        }
            }
        }