} // 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); } }
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 } } } } }
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, typeof(int))) { // pre-callbacks if (HasCallbacks(TypeModel.CallbackType.BeforeDeserialize)) { if (Helpers.IsValueType(ExpectedType)) { 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.LoadReader(true); if (isExtensible) { ctx.LoadValue(loc); ctx.EmitCall(typeof(ProtoReader).GetMethod("AppendExtensionData", new[] { Compiler.ReaderUtil.ByRefStateType, typeof(IExtensible) })); } else { ctx.EmitCall(typeof(ProtoReader).GetMethod("SkipField", Compiler.ReaderUtil.StateTypeArray)); } ctx.MarkLabel(@continue); ctx.EmitBasicRead("ReadFieldHeader", 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 (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); } } }
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(); } } }
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); // the field could be a backing field that needs to be raised back to // the property if we're doing a full compile MemberInfo member = field; ctx.CheckAccessibility(ref member); bool writeValue = member is FieldInfo; if (writeValue) { if (Tail.ReturnsValue) { var localType = PropertyDecorator.ChooseReadLocalType(field.FieldType, Tail.ExpectedType); using Compiler.Local newVal = new Compiler.Local(ctx, localType); ctx.StoreValue(newVal); if (field.FieldType.IsValueType) { 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); // cast if needed (this is mostly for ReadMap/ReadRepeated) if (!field.FieldType.IsValueType && !localType.IsValueType && !field.FieldType.IsAssignableFrom(localType)) { ctx.Cast(field.FieldType); } ctx.StoreValue(field); ctx.MarkLabel(allDone); } } } else { // can't use result if (Tail.ReturnsValue) { ctx.DiscardValue(); } } }
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) { 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); if (Tail.ReturnsValue) { using (Compiler.Local newVal = new Compiler.Local(ctx, field.FieldType)) { ctx.StoreValue(newVal); if (field.FieldType.IsValueType) { 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); ctx.StoreValue(field); ctx.MarkLabel(allDone); } } } } }
private bool EmitDedicatedMethod(Compiler.CompilerContext ctx, Compiler.Local valueFrom, bool read) { MethodBuilder method = ctx.GetDedicatedMethod(key, read); if (method == null) { return(false); } using (Compiler.Local val = ctx.GetLocalWithValue(type, valueFrom)) using (Compiler.Local token = new ProtoBuf.Compiler.Local(ctx, typeof(SubItemToken))) { Type rwType = read ? typeof(ProtoReader) : typeof(ProtoWriter); if (read) { ctx.LoadReader(true); } else { // 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.LoadValue(val); } ctx.LoadWriter(true); } ctx.EmitCall(Helpers.GetStaticMethod(rwType, "StartSubItem", read ? ProtoReader.State.ReaderStateTypeArray : new Type[] { typeof(object), rwType, ProtoWriter.ByRefStateType })); ctx.StoreValue(token); if (read) { ctx.LoadReader(true); ctx.LoadValue(val); } else { ctx.LoadWriter(true); ctx.LoadValue(val); } 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(type); } ctx.LoadValue(token); if (read) { ctx.LoadReader(true); ctx.EmitCall(Helpers.GetStaticMethod(rwType, "EndSubItem", new Type[] { typeof(SubItemToken), rwType, ProtoReader.State.ByRefStateType })); } else { ctx.LoadWriter(true); ctx.EmitCall(ProtoWriter.GetStaticMethod("EndSubItem")); } } return(true); }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (Compiler.Local oldValue = ctx.GetLocalWithValue(ExpectedType, valueFrom)) using (Compiler.Local token = new Compiler.Local(ctx, typeof(SubItemToken))) using (Compiler.Local field = new Compiler.Local(ctx, typeof(int))) { ctx.LoadReader(true); ctx.EmitCall(typeof(ProtoReader).GetMethod("StartSubItem", Compiler.ReaderUtil.ReaderStateTypeArray)); ctx.StoreValue(token); Compiler.CodeLabel next = ctx.DefineLabel(), processField = ctx.DefineLabel(), end = ctx.DefineLabel(); ctx.MarkLabel(next); ctx.EmitBasicRead("ReadFieldHeader", typeof(int)); ctx.CopyValue(); ctx.StoreValue(field); ctx.LoadValue(Tag); // = 1 - process ctx.BranchIfEqual(processField, true); ctx.LoadValue(field); ctx.LoadValue(1); // < 1 - exit ctx.BranchIfLess(end, false); // default: skip ctx.LoadReader(true); ctx.EmitCall(typeof(ProtoReader).GetMethod("SkipField", Compiler.ReaderUtil.StateTypeArray)); ctx.Branch(next, true); // process ctx.MarkLabel(processField); if (Tail.RequiresOldValue) { if (Helpers.IsValueType(ExpectedType)) { ctx.LoadAddress(oldValue, ExpectedType); ctx.EmitCall(ExpectedType.GetMethod("GetValueOrDefault", Helpers.EmptyTypes)); } else { ctx.LoadValue(oldValue); } } Tail.EmitRead(ctx, null); // note we demanded always returns a value if (Helpers.IsValueType(ExpectedType)) { ctx.EmitCtor(ExpectedType, Tail.ExpectedType); // re-nullable<T> it } ctx.StoreValue(oldValue); ctx.Branch(next, false); // outro ctx.MarkLabel(end); ctx.LoadValue(token); ctx.LoadReader(true); ctx.EmitCall(typeof(ProtoReader).GetMethod("EndSubItem", new[] { typeof(SubItemToken), typeof(ProtoReader), Compiler.ReaderUtil.ByRefStateType })); ctx.LoadValue(oldValue); // load the old value } }
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 (Helpers.IsValueType(ExpectedType) && valueFrom == 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, overrideType); // stack is [new-value] if (writeValue) { var newType = overrideType != null ? overrideType : property.PropertyType; using (Compiler.Local newVal = new Compiler.Local(ctx, newType)) { ctx.StoreValue(newVal); // stack is empty Compiler.CodeLabel allDone = new Compiler.CodeLabel(); // <=== default structs if (!Helpers.IsValueType(propertyType)) { // 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 if (overrideType != null) { ctx.Cast(property.PropertyType); } if (shadowSetter == null) { ctx.StoreValue(property); // empty } else { ctx.EmitCall(shadowSetter); // empty } if (!Helpers.IsValueType(propertyType)) { ctx.MarkLabel(allDone); } } } else { // don't want return value; drop it if anything there // stack is [new-value] if (Tail.ReturnsValue) { ctx.DiscardValue(); } } } }
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")); } }