private void WriteFieldHandler( Compiler.CompilerContext ctx, Type expected, Compiler.Local loc, Compiler.CodeLabel handler, Compiler.CodeLabel @continue, IProtoSerializer serializer) { ctx.MarkLabel(handler); Type serType = serializer.ExpectedType; if (serType == forType) { EmitCreateIfNull(ctx, loc); serializer.EmitRead(ctx, loc); } else { //RuntimeTypeModel rtm = (RuntimeTypeModel)ctx.Model; if (((IProtoTypeSerializer)serializer).CanCreateInstance()) { Compiler.CodeLabel allDone = ctx.DefineLabel(); ctx.LoadValue(loc); ctx.BranchIfFalse(allDone, false); // null is always ok ctx.LoadValue(loc); ctx.TryCast(serType); ctx.BranchIfTrue(allDone, false); // not null, but of the correct type // otherwise, need to convert it ctx.LoadReaderWriter(); ctx.LoadValue(loc); ((IProtoTypeSerializer)serializer).EmitCreateInstance(ctx); ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("Merge")); ctx.Cast(expected); ctx.StoreValue(loc); // Merge always returns a value // nothing needs doing ctx.MarkLabel(allDone); } ctx.LoadValue(loc); ctx.Cast(serType); serializer.EmitRead(ctx, null); } if (serializer.ReturnsValue) { // update the variable ctx.StoreValue(loc); } ctx.Branch(@continue, false); // "continue" }
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { Helpers.DebugAssert(valueFrom != null); // don't support stack-head for this using (Compiler.Local converted = new Compiler.Local(ctx, declaredType)) // declare/re-use local { ctx.LoadValue(valueFrom); // load primary onto stack ctx.EmitCall(toTail); // static convert op, primary-to-surrogate ctx.StoreValue(converted); // store into surrogate local rootTail.EmitRead(ctx, converted); // downstream processing against surrogate local ctx.LoadValue(converted); // load from surrogate local ctx.EmitCall(fromTail); // static convert op, surrogate-to-primary ctx.StoreValue(valueFrom); // store back into primary } }
private static void WriteEnumValue(Compiler.CompilerContext ctx, ProtoTypeCode typeCode, Compiler.CodeLabel handler, Compiler.CodeLabel @continue, object value, Compiler.Local local) { ctx.MarkLabel(handler); WriteEnumValue(ctx, typeCode, value); ctx.StoreValue(local); ctx.Branch(@continue, false); // "continue" }
private void EmitCreateIfNull(Compiler.CompilerContext ctx, Compiler.Local storage) { Helpers.DebugAssert(storage != null); if (!ExpectedType.IsValueType) { 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, forType); } ctx.StoreValue(storage); ctx.MarkLabel(afterNullCheck); } }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { bool writeValue; SanityCheck(property, Tail, out writeValue, ctx.NonPublic); if (ExpectedType.IsValueType && valueFrom == null) { throw new InvalidOperationException("Attempt to mutate struct on the head of the stack; changes would be lost"); } ctx.LoadAddress(valueFrom, ExpectedType); // stack is: old-addr if (writeValue && Tail.RequiresOldValue) { // need to read and write ctx.CopyValue(); } // stack is: [old-addr]|old-addr if (Tail.RequiresOldValue) { ctx.LoadValue(property); // stack is: [old-addr]|old-value } ctx.ReadNullCheckedTail(property.PropertyType, Tail, null); // stack is [old-addr]|[new-value] if (writeValue) { // stack is old-addr|new-value ctx.StoreValue(property); } else { // don't want return value; drop it if anything there // stack is [new-value] if (Tail.ReturnsValue) { ctx.DiscardValue(); } } }
void IRuntimeProtoSerializerNode.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { // Debug.Assert(valueFrom is object, "surrogate value on stack-head"); // don't support stack-head for this using Compiler.Local converted = rootTail.RequiresOldValue ? new Compiler.Local(ctx, declaredType) : null; if (rootTail.RequiresOldValue) { ctx.LoadValue(valueFrom); // load primary onto stack ctx.EmitCall(toTail); // static convert op, primary-to-surrogate ctx.StoreValue(converted); // store into surrogate local } rootTail.EmitRead(ctx, converted); // downstream processing against surrogate local ctx.LoadValue(converted); // load from surrogate local ctx.EmitCall(fromTail); // static convert op, surrogate-to-primary ctx.StoreValue(valueFrom); // store back into primary }
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); } } } }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom)) { if (Tail.RequiresOldValue) { ctx.LoadAddress(loc, ExpectedType); ctx.LoadValue(field); } // value is either now on the stack or not needed ctx.ReadNullCheckedTail(field.FieldType, Tail, null, overrideType); if (Tail.ReturnsValue) { var newType = overrideType != null ? overrideType : field.FieldType; using (Compiler.Local newVal = new Compiler.Local(ctx, newType)) { ctx.StoreValue(newVal); if (Helpers.IsValueType(field.FieldType)) { ctx.LoadAddress(loc, ExpectedType); ctx.LoadValue(newVal); ctx.StoreValue(field); } else { Compiler.CodeLabel allDone = ctx.DefineLabel(); ctx.LoadValue(newVal); ctx.BranchIfFalse(allDone, true); // interpret null as "don't assign" ctx.LoadAddress(loc, ExpectedType); ctx.LoadValue(newVal); if (overrideType != null) { ctx.Cast(field.FieldType); } ctx.StoreValue(field); ctx.MarkLabel(allDone); } } } } }
private void EmitCreateIfNull(Compiler.CompilerContext ctx, Type type, Compiler.Local storage) { Helpers.DebugAssert(storage != null); if (!type.IsValueType) { Compiler.CodeLabel afterNullCheck = ctx.DefineLabel(); ctx.LoadValue(storage); ctx.BranchIfTrue(afterNullCheck, true); // different ways of creating a new instance bool callNoteObject = true; if (factory != null) { EmitInvokeCallback(ctx, factory, false); } else if (!useConstructor) { // DataContractSerializer style ctx.LoadValue(constructType); ctx.EmitCall(ctx.MapType(typeof(BclHelpers)).GetMethod("GetUninitializedObject")); ctx.Cast(forType); } else if (constructType.IsClass && hasConstructor) { // XmlSerializer style ctx.EmitCtor(constructType); } else { ctx.LoadValue(type); ctx.EmitCall(ctx.MapType(typeof(TypeModel)).GetMethod("ThrowCannotCreateInstance", BindingFlags.Static | BindingFlags.Public)); ctx.LoadNullRef(); callNoteObject = false; } if (callNoteObject) { // track root object creation ctx.CopyValue(); ctx.LoadReaderWriter(); ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("NoteObject", BindingFlags.Static | BindingFlags.Public)); } if (baseCtorCallbacks != null) { for (int i = 0; i < baseCtorCallbacks.Length; i++) { EmitInvokeCallback(ctx, baseCtorCallbacks[i], true); } } if (callbacks != null) { EmitInvokeCallback(ctx, callbacks.BeforeDeserialize, true); } ctx.StoreValue(storage); ctx.MarkLabel(afterNullCheck); } }
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); } } } }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom)) { ctx.LoadAddress(loc, ExpectedType); if (Tail.RequiresOldValue) { ctx.CopyValue(); ctx.LoadValue(field); } // value is either now on the stack or not needed ctx.ReadNullCheckedTail(field.FieldType, Tail, null); if (Tail.ReturnsValue) { if (field.FieldType.IsValueType) { // stack is now the return value ctx.StoreValue(field); } else { Compiler.CodeLabel hasValue = ctx.DefineLabel(), allDone = ctx.DefineLabel(); ctx.CopyValue(); ctx.BranchIfTrue(hasValue, true); // interpret null as "don't assign" // no value, discard ctx.DiscardValue(); ctx.DiscardValue(); ctx.Branch(allDone, true); ctx.MarkLabel(hasValue); ctx.StoreValue(field); ctx.MarkLabel(allDone); } } else { ctx.DiscardValue(); } } }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom)) { ctx.LoadAddress(loc, ExpectedType); if (Tail.RequiresOldValue) { ctx.CopyValue(); ctx.LoadValue(field); } // value is either now on the stack or not needed ctx.ReadNullCheckedTail(field.FieldType, Tail, null); // stack is now the return value ctx.StoreValue(field); } }
bool EmitDedicatedMethod(Compiler.CompilerContext ctx, Compiler.Local valueFrom, bool read) { #if SILVERLIGHT return(false); #else MethodBuilder method = ctx.GetDedicatedMethod(key, read); if (method == null) { return(false); } using (Compiler.Local token = new UcAsp.RPC.ProtoBuf.Compiler.Local(ctx, ctx.MapType(typeof(SubItemToken)))) { Type rwType = ctx.MapType(read ? typeof(ProtoReader) : typeof(ProtoWriter)); ctx.LoadValue(valueFrom); if (!read) // write requires the object for StartSubItem; read doesn't { // (if recursion-check is disabled [subtypes] then null is fine too) if (Helpers.IsValueType(type) || !recursionCheck) { ctx.LoadNullRef(); } else { ctx.CopyValue(); } } ctx.LoadReaderWriter(); ctx.EmitCall(Helpers.GetStaticMethod(rwType, "StartSubItem", read ? new Type[] { rwType } : new Type[] { ctx.MapType(typeof(object)), rwType })); ctx.StoreValue(token); // note: value already on the stack ctx.LoadReaderWriter(); ctx.EmitCall(method); // handle inheritance (we will be calling the *base* version of things, // but we expect Read to return the "type" type) if (read && type != method.ReturnType) { ctx.Cast(this.type); } ctx.LoadValue(token); ctx.LoadReaderWriter(); ctx.EmitCall(Helpers.GetStaticMethod(rwType, "EndSubItem", new Type[] { ctx.MapType(typeof(SubItemToken)), rwType })); } return(true); #endif }
private void EmitCreateIfNull(Compiler.CompilerContext ctx, Type type, Compiler.Local storage) { Helpers.DebugAssert(storage != null); if (!type.IsValueType) { Compiler.CodeLabel afterNullCheck = ctx.DefineLabel(); ctx.LoadValue(storage); ctx.BranchIfTrue(afterNullCheck, true); // different ways of creating a new instance if (!useConstructor) { // DataContractSerializer style ctx.LoadValue(forType); ctx.EmitCall(typeof(BclHelpers).GetMethod("GetUninitializedObject")); ctx.Cast(forType); } else if (type.IsClass && !type.IsAbstract && ( (type.GetConstructor( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, Helpers.EmptyTypes, null)) != null)) { // XmlSerializer style ctx.EmitCtor(type); } else { ctx.LoadValue(type); ctx.EmitCall(typeof(TypeModel).GetMethod("ThrowCannotCreateInstance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)); ctx.LoadNullRef(); } if (baseCtorCallbacks != null) { for (int i = 0; i < baseCtorCallbacks.Length; i++) { EmitInvokeCallback(ctx, baseCtorCallbacks[i]); } } if (callbacks != null) { EmitInvokeCallback(ctx, callbacks.BeforeDeserialize); } ctx.StoreValue(storage); ctx.MarkLabel(afterNullCheck); } }
bool EmitDedicatedMethod(Compiler.CompilerContext ctx, Compiler.Local valueFrom, bool read) { System.Reflection.Emit.MethodBuilder method = ctx.GetDedicatedMethod(key, read); if (method == null) { return(false); } using (Compiler.Local token = new ProtoBuf.Compiler.Local(ctx, typeof(SubItemToken))) { Type rwType = read ? typeof(ProtoReader) : typeof(ProtoWriter); ctx.LoadValue(valueFrom); if (!read) // write requires the object for StartSubItem; read doesn't { // (if recursion-check is disabled [subtypes] then null is fine too) if (type.IsValueType || !recursionCheck) { ctx.LoadNullRef(); } else { ctx.CopyValue(); } } ctx.LoadReaderWriter(); ctx.EmitCall(rwType.GetMethod("StartSubItem")); ctx.StoreValue(token); // note: value already on the stack ctx.LoadReaderWriter(); ctx.EmitCall(method); // handle inheritance (we will be calling the *base* version of things, // but we expect Read to return the "type" type) if (read && type != method.ReturnType) { ctx.Cast(this.type); } ctx.LoadValue(token); ctx.LoadReaderWriter(); ctx.EmitCall(rwType.GetMethod("EndSubItem")); } return(true); }
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); } } } }
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (Compiler.Local valOrNull = ctx.GetLocalWithValue(expectedType, valueFrom)) using (Compiler.Local token = new Compiler.Local(ctx, ctx.MapType(typeof(SubItemToken)))) { ctx.LoadNullRef(); ctx.LoadReaderWriter(); ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("StartSubItem")); ctx.StoreValue(token); if (expectedType.IsValueType) { ctx.LoadAddress(valOrNull, expectedType); ctx.LoadValue(expectedType.GetProperty("HasValue")); } else { ctx.LoadValue(valOrNull); } Compiler.CodeLabel @end = ctx.DefineLabel(); ctx.BranchIfFalse(@end, false); if (expectedType.IsValueType) { ctx.LoadAddress(valOrNull, expectedType); ctx.EmitCall(expectedType.GetMethod("GetValueOrDefault", Helpers.EmptyTypes)); } else { ctx.LoadValue(valOrNull); } Tail.EmitWrite(ctx, null); ctx.MarkLabel(@end); ctx.LoadValue(token); ctx.LoadReaderWriter(); ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("EndSubItem")); } }
private void WriteFieldHandler( Compiler.CompilerContext ctx, Type expected, Compiler.Local loc, Compiler.CodeLabel handler, Compiler.CodeLabel @continue, IProtoSerializer serializer) { ctx.MarkLabel(handler); if (serializer.ExpectedType == forType) { EmitCreateIfNull(ctx, expected, loc); serializer.EmitRead(ctx, loc); } else { ctx.LoadValue(loc); ctx.Cast(serializer.ExpectedType); serializer.EmitRead(ctx, null); } if (serializer.ReturnsValue) { // update the variable ctx.StoreValue(loc); } ctx.Branch(@continue, false); // "continue" }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { bool writeValue; SanityCheck(ctx.Model, property, Tail, out writeValue, ctx.NonPublic, ctx.AllowInternal(property)); if (ExpectedType.IsValueType && valueFrom == null) { throw new InvalidOperationException("Attempt to mutate struct on the head of the stack; changes would be lost"); } ctx.LoadAddress(valueFrom, ExpectedType); // stack is: old-addr if (writeValue && Tail.RequiresOldValue) { // need to read and write ctx.CopyValue(); } // stack is: [old-addr]|old-addr if (Tail.RequiresOldValue) { ctx.LoadValue(property); // stack is: [old-addr]|old-value } Type propertyType = property.PropertyType; ctx.ReadNullCheckedTail(propertyType, Tail, null); // stack is [old-addr]|[new-value] if (writeValue) { // stack is old-addr|new-value Compiler.CodeLabel @skip = new Compiler.CodeLabel(), allDone = new Compiler.CodeLabel(); // <=== default structs if (!propertyType.IsValueType) { // if the tail returns a null, intepret that as *no assign* ctx.CopyValue(); // old-addr|new-value|new-value @skip = ctx.DefineLabel(); allDone = ctx.DefineLabel(); ctx.BranchIfFalse(@skip, true); // old-addr|new-value } if (shadowSetter == null) { ctx.StoreValue(property); } else { ctx.EmitCall(shadowSetter); } if (!propertyType.IsValueType) { ctx.Branch(allDone, true); ctx.MarkLabel(@skip); // old-addr|new-value ctx.DiscardValue(); ctx.DiscardValue(); ctx.MarkLabel(allDone); } } else { // don't want return value; drop it if anything there // stack is [new-value] if (Tail.ReturnsValue) { ctx.DiscardValue(); } } }
private static void EmitReadAndAddItem(Compiler.CompilerContext ctx, Compiler.Local list, IProtoSerializer tail, MethodInfo add, bool castListForAdd) { ctx.LoadAddress(list, list.Type); // needs to be the reference in case the list is value-type (static-call) if (castListForAdd) { ctx.Cast(add.DeclaringType); } Type itemType = tail.ExpectedType; bool tailReturnsValue = tail.ReturnsValue; if (tail.RequiresOldValue) { if (itemType.IsValueType || !tailReturnsValue) { // going to need a variable using (Compiler.Local item = new Compiler.Local(ctx, itemType)) { if (itemType.IsValueType) { // initialise the struct ctx.LoadAddress(item, itemType); ctx.EmitCtor(itemType); } else { // assign null ctx.LoadNullRef(); ctx.StoreValue(item); } tail.EmitRead(ctx, item); if (!tailReturnsValue) { ctx.LoadValue(item); } } } else { // no variable; pass the null on the stack and take the value *off* the stack ctx.LoadNullRef(); tail.EmitRead(ctx, null); } } else { if (tailReturnsValue) { // out only (on the stack); just emit it tail.EmitRead(ctx, null); } else { // doesn't take anything in nor return anything! WTF? throw new InvalidOperationException(); } } // our "Add" is chosen either to take the correct type, or to take "object"; // we may need to box the value Type addParamType = add.GetParameters()[0].ParameterType; if (addParamType != itemType) { if (addParamType == ctx.MapType(typeof(object))) { ctx.CastToObject(itemType); } else if (Helpers.GetUnderlyingType(addParamType) == itemType) { // list is nullable ConstructorInfo ctor = Helpers.GetConstructor(addParamType, new Type[] { itemType }, false); ctx.EmitCtor(ctor); // the itemType on the stack is now a Nullable<ItemType> } else { throw new InvalidOperationException("Conflicting item/add type"); } } ctx.EmitCall(add); if (add.ReturnType != ctx.MapType(typeof(void))) { ctx.DiscardValue(); } }
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { ProtoTypeCode typeCode = GetTypeCode(); if (map == null) { ctx.EmitBasicRead("ReadInt32", ctx.MapType(typeof(int))); ctx.ConvertFromInt32(typeCode, false); } else { int[] wireValues = new int[map.Length]; object[] values = new object[map.Length]; for (int i = 0; i < map.Length; i++) { wireValues[i] = map[i].WireValue; values[i] = map[i].RawValue; } using (Compiler.Local result = new Compiler.Local(ctx, ExpectedType)) using (Compiler.Local wireValue = new Compiler.Local(ctx, ctx.MapType(typeof(int)))) { ctx.EmitBasicRead("ReadInt32", ctx.MapType(typeof(int))); ctx.StoreValue(wireValue); Compiler.CodeLabel @continue = ctx.DefineLabel(); foreach (BasicList.Group group in BasicList.GetContiguousGroups(wireValues, values)) { Compiler.CodeLabel tryNextGroup = ctx.DefineLabel(); int groupItemCount = group.Items.Count; if (groupItemCount == 1) { // discreet group; use an equality test ctx.LoadValue(wireValue); ctx.LoadValue(group.First); Compiler.CodeLabel processThisValue = ctx.DefineLabel(); ctx.BranchIfEqual(processThisValue, true); ctx.Branch(tryNextGroup, false); WriteEnumValue(ctx, typeCode, processThisValue, @continue, group.Items[0], @result); } else { // implement as a jump-table-based switch ctx.LoadValue(wireValue); 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(tryNextGroup, false); for (int i = 0; i < groupItemCount; i++) { WriteEnumValue(ctx, typeCode, jmp[i], @continue, group.Items[i], @result); } } ctx.MarkLabel(tryNextGroup); } // throw source.CreateEnumException(ExpectedType, wireValue); ctx.LoadReaderWriter(); ctx.LoadValue(ExpectedType); ctx.LoadValue(wireValue); ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("ThrowEnumException")); ctx.MarkLabel(@continue); ctx.LoadValue(result); } } }
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); } } } }
private void WriteFieldHandler( Compiler.CompilerContext ctx, Type expected, Compiler.Local loc, Compiler.CodeLabel handler, Compiler.CodeLabel @continue, IProtoSerializer serializer) { ctx.MarkLabel(handler); Type serType = serializer.ExpectedType; if (serType == ExpectedType) { EmitCreateIfNull(ctx, loc); serializer.EmitRead(ctx, loc); } else { //RuntimeTypeModel rtm = (RuntimeTypeModel)ctx.Model; if (((IProtoTypeSerializer)serializer).CanCreateInstance()) { Compiler.CodeLabel allDone = ctx.DefineLabel(); ctx.LoadValue(loc); ctx.BranchIfFalse(allDone, false); // null is always ok ctx.LoadValue(loc); ctx.TryCast(serType); ctx.BranchIfTrue(allDone, false); // not null, but of the correct type // otherwise, need to convert it ctx.LoadReader(false); ctx.LoadValue(loc); ((IProtoTypeSerializer)serializer).EmitCreateInstance(ctx); ctx.EmitCall(typeof(ProtoReader).GetMethod("Merge", new[] { typeof(ProtoReader), typeof(object), typeof(object) })); ctx.Cast(expected); ctx.StoreValue(loc); // Merge always returns a value // nothing needs doing ctx.MarkLabel(allDone); } if (Helpers.IsValueType(serType)) { Compiler.CodeLabel initValue = ctx.DefineLabel(); Compiler.CodeLabel hasValue = ctx.DefineLabel(); using (Compiler.Local emptyValue = new Compiler.Local(ctx, serType)) { ctx.LoadValue(loc); ctx.BranchIfFalse(initValue, false); ctx.LoadValue(loc); ctx.CastFromObject(serType); ctx.Branch(hasValue, false); ctx.MarkLabel(initValue); ctx.InitLocal(serType, emptyValue); ctx.LoadValue(emptyValue); ctx.MarkLabel(hasValue); } } else { ctx.LoadValue(loc); ctx.Cast(serType); } serializer.EmitRead(ctx, null); } if (serializer.ReturnsValue) { // update the variable if (Helpers.IsValueType(serType)) { // but box it first in case of value type ctx.CastToObject(serType); } ctx.StoreValue(loc); } ctx.Branch(@continue, false); // "continue" }
public void EmitRead(Compiler.CompilerContext ctx, Compiler.Local incoming) { using (Compiler.Local objValue = ctx.GetLocalWithValue(ExpectedType, incoming)) { Compiler.Local[] locals = new Compiler.Local[members.Length]; try { 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].MemberType) { case MemberTypes.Field: ctx.LoadValue((FieldInfo)members[i]); break; case MemberTypes.Property: ctx.LoadValue((PropertyInfo)members[i]); 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.ReturnsValue) { 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); } for (int i = 0; i < locals.Length; i++) { ctx.LoadValue(locals[i]); } ctx.EmitCtor(ctor); ctx.StoreValue(objValue); } finally { for (int i = 0; i < locals.Length; i++) { if (locals[i] != null) { locals[i].Dispose(); // release for re-use } } } } }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (Compiler.Local oldList = AppendToCollection ? ctx.GetLocalWithValue(ExpectedType, valueFrom) : null) using (Compiler.Local builder = new Compiler.Local(ctx, builderFactory.ReturnType)) { ctx.EmitCall(builderFactory); ctx.StoreValue(builder); if (AppendToCollection) { Compiler.CodeLabel done = ctx.DefineLabel(); if (!Helpers.IsValueType(ExpectedType)) { ctx.LoadValue(oldList); ctx.BranchIfFalse(done, false); // old value null; nothing to add } #if COREFX TypeInfo typeInfo = ExpectedType.GetTypeInfo(); #else Type typeInfo = ExpectedType; #endif PropertyInfo prop = Helpers.GetProperty(typeInfo, "Length", false); if (prop == null) { prop = Helpers.GetProperty(typeInfo, "Count", false); } #if !NO_GENERICS if (prop == null) { prop = Helpers.GetProperty(ResolveIReadOnlyCollection(ExpectedType, Tail.ExpectedType), "Count", false); } #endif ctx.LoadAddress(oldList, oldList.Type); ctx.EmitCall(Helpers.GetGetMethod(prop, false, false)); ctx.BranchIfFalse(done, false); // old list is empty; nothing to add Type voidType = ctx.MapType(typeof(void)); if (addRange != null) { ctx.LoadValue(builder); ctx.LoadValue(oldList); 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(oldList, 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(builder, builder.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); } EmitReadList(ctx, builder, Tail, add, packedWireType, false); ctx.LoadAddress(builder, builder.Type); ctx.EmitCall(finish); if (ExpectedType != finish.ReturnType) { ctx.Cast(ExpectedType); } } }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { SanityCheck(property, Tail, out bool writeValue, ctx.NonPublic, ctx.AllowInternal(property)); if (ExpectedType.IsValueType && valueFrom is null) { throw new InvalidOperationException("Attempt to mutate struct on the head of the stack; changes would be lost"); } using Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom); if (Tail.RequiresOldValue) { ctx.LoadAddress(loc, ExpectedType); // stack is: old-addr ctx.LoadValue(property); // stack is: old-value } Type propertyType = property.PropertyType; ctx.ReadNullCheckedTail(propertyType, Tail, null); // stack is [new-value] if (writeValue) { var localType = ChooseReadLocalType(property.PropertyType, Tail.ExpectedType); using Compiler.Local newVal = new Compiler.Local(ctx, localType); ctx.StoreValue(newVal); // stack is empty Compiler.CodeLabel allDone = new Compiler.CodeLabel(); // <=== default structs if (!localType.IsValueType) { // if the tail returns a null, intepret that as *no assign* allDone = ctx.DefineLabel(); ctx.LoadValue(newVal); // stack is: new-value ctx.BranchIfFalse(@allDone, true); // stack is empty } // assign the value ctx.LoadAddress(loc, ExpectedType); // parent-addr ctx.LoadValue(newVal); // parent-obj|new-value // cast if needed (this is mostly for ReadMap/ReadRepeated) if (!property.PropertyType.IsValueType && !localType.IsValueType && !property.PropertyType.IsAssignableFrom(localType)) { ctx.Cast(property.PropertyType); } if (shadowSetter is null) { ctx.StoreValue(property); // empty } else { ctx.EmitCall(shadowSetter); // empty } if (!propertyType.IsValueType) { ctx.MarkLabel(allDone); } } else { // don't want return value; drop it if anything there // stack is [new-value] if (Tail.ReturnsValue) { ctx.DiscardValue(); } } }
} // updates field directly #if FEAT_COMPILER void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { Type expected = ExpectedType; using (Compiler.Local loc = ctx.GetLocalWithValue(expected, valueFrom)) { // pre-callbacks EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.BeforeSerialize); Compiler.CodeLabel startFields = ctx.DefineLabel(); // inheritance if (CanHaveInheritance) { for (int i = 0; i < serializers.Length; i++) { IProtoSerializer ser = serializers[i]; Type serType = ser.ExpectedType; if (serType != forType) { Compiler.CodeLabel ifMatch = ctx.DefineLabel(), nextTest = ctx.DefineLabel(); ctx.LoadValue(loc); ctx.TryCast(serType); ctx.CopyValue(); ctx.BranchIfTrue(ifMatch, true); ctx.DiscardValue(); ctx.Branch(nextTest, true); ctx.MarkLabel(ifMatch); ser.EmitWrite(ctx, null); ctx.Branch(startFields, false); ctx.MarkLabel(nextTest); } } if (constructType != null && constructType != forType) { using (Compiler.Local actualType = new Compiler.Local(ctx, ctx.MapType(typeof(System.Type)))) { // would have jumped to "fields" if an expected sub-type, so two options: // a: *exactly* that type, b: an *unexpected* type ctx.LoadValue(loc); ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("GetType")); ctx.CopyValue(); ctx.StoreValue(actualType); ctx.LoadValue(forType); ctx.BranchIfEqual(startFields, true); ctx.LoadValue(actualType); ctx.LoadValue(constructType); ctx.BranchIfEqual(startFields, true); } } else { // would have jumped to "fields" if an expected sub-type, so two options: // a: *exactly* that type, b: an *unexpected* type ctx.LoadValue(loc); ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("GetType")); ctx.LoadValue(forType); ctx.BranchIfEqual(startFields, true); } // unexpected, then... note that this *might* be a proxy, which // is handled by ThrowUnexpectedSubtype ctx.LoadValue(forType); ctx.LoadValue(loc); ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("GetType")); ctx.EmitCall(ctx.MapType(typeof(TypeModel)).GetMethod("ThrowUnexpectedSubtype", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)); } // fields ctx.MarkLabel(startFields); for (int i = 0; i < serializers.Length; i++) { IProtoSerializer ser = serializers[i]; if (ser.ExpectedType == forType) { ser.EmitWrite(ctx, loc); } } // extension data if (isExtensible) { ctx.LoadValue(loc); ctx.LoadReaderWriter(); ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("AppendExtensionData")); } // post-callbacks EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.AfterSerialize); } }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (Compiler.Local oldList = AppendToCollection ? ctx.GetLocalWithValue(ExpectedType, valueFrom) : null) using (Compiler.Local builder = new Compiler.Local(ctx, builderFactory.ReturnType)) { ctx.EmitCall(builderFactory); ctx.StoreValue(builder); if (AppendToCollection) { Compiler.CodeLabel done = ctx.DefineLabel(); if (!Helpers.IsValueType(ExpectedType)) { ctx.LoadValue(oldList); ctx.BranchIfFalse(done, false); // old value null; nothing to add } ctx.LoadAddress(oldList, oldList.Type); if (isEmpty != null) { ctx.EmitCall(Helpers.GetGetMethod(isEmpty, false, false)); ctx.BranchIfTrue(done, false); // old list is empty; nothing to add } else { ctx.EmitCall(Helpers.GetGetMethod(length, false, false)); ctx.BranchIfFalse(done, false); // old list is empty; nothing to add } Type voidType = typeof(void); if (addRange != null) { ctx.LoadValue(builder); ctx.LoadValue(oldList); ctx.EmitCall(addRange); if (addRange.ReturnType != null && add.ReturnType != voidType) { ctx.DiscardValue(); } } else { // loop and call Add repeatedly MethodInfo moveNext, current, getEnumerator = GetEnumeratorInfo(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(oldList, 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(builder, builder.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); } EmitReadList(ctx, builder, Tail, add, packedWireType, false); ctx.LoadAddress(builder, builder.Type); ctx.EmitCall(finish); if (ExpectedType != finish.ReturnType) { ctx.Cast(ExpectedType); } } }
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { Type expected = ExpectedType; Helpers.DebugAssert(valueFrom != null); using (Compiler.Local loc = ctx.GetLocalWithValue(expected, valueFrom)) using (Compiler.Local fieldNumber = new Compiler.Local(ctx, ctx.MapType(typeof(int)))) { // 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, expected, 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, expected, 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); if (valueFrom != null && !loc.IsSame(valueFrom)) { ctx.LoadValue(loc); ctx.Cast(valueFrom.Type); ctx.StoreValue(valueFrom); } } }
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); } } } }