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" }
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); } }
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); } }
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); } }
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); } } } }
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(); } } } }
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); } } } }