void IProtoTypeSerializer.EmitCallback(Compiler.CompilerContext ctx, Compiler.Local valueFrom, TypeModel.CallbackType callbackType) { Helpers.DebugAssert(((IProtoTypeSerializer)this).HasCallbacks(callbackType), "Shouldn't be calling this if there is nothing to do"); MethodInfo method = callbacks == null ? null : callbacks[callbackType]; ctx.LoadValue(valueFrom); EmitInvokeCallback(ctx, method); Compiler.CodeLabel @break = ctx.DefineLabel(); if (CanHaveInheritance) { for (int i = 0; i < serializers.Length; i++) { IProtoSerializer ser = serializers[i]; IProtoTypeSerializer typeser; if (ser.ExpectedType != forType && (typeser = (IProtoTypeSerializer)ser).HasCallbacks(callbackType)) { Compiler.CodeLabel ifMatch = ctx.DefineLabel(), nextTest = ctx.DefineLabel(); ctx.CopyValue(); ctx.TryCast(ser.ExpectedType); ctx.CopyValue(); ctx.BranchIfTrue(ifMatch, true); ctx.DiscardValue(); ctx.Branch(nextTest, false); ctx.MarkLabel(ifMatch); typeser.EmitCallback(ctx, null, callbackType); ctx.Branch(@break, false); ctx.MarkLabel(nextTest); } } } ctx.MarkLabel(@break); ctx.DiscardValue(); }
void IProtoTypeSerializer.EmitCallback(Compiler.CompilerContext ctx, Compiler.Local valueFrom, TypeModel.CallbackType callbackType) { using (ctx.StartDebugBlockAuto(this)) { bool actuallyHasInheritance = false; if (CanHaveInheritance) { for (int i = 0; i < _serializers.Length; i++) { IProtoSerializer ser = _serializers[i]; if (ser.ExpectedType != ExpectedType && ((ser as IProtoTypeSerializer)?.HasCallbacks(callbackType) ?? false)) { actuallyHasInheritance = true; } } } Helpers.DebugAssert(((IProtoTypeSerializer)this).HasCallbacks(callbackType), "Shouldn't be calling this if there is nothing to do"); MethodInfo method = _callbacks?[callbackType]; if (method == null && !actuallyHasInheritance) { return; } ctx.LoadAddress(valueFrom, ExpectedType); EmitInvokeCallback(ctx, method, actuallyHasInheritance, null, ExpectedType); if (actuallyHasInheritance) { Compiler.CodeLabel @break = ctx.DefineLabel(); for (int i = 0; i < _serializers.Length; i++) { IProtoSerializer ser = _serializers[i]; IProtoTypeSerializer typeser; Type serType = ser.ExpectedType; if (serType != ExpectedType && (typeser = (IProtoTypeSerializer)ser).HasCallbacks(callbackType)) { Compiler.CodeLabel ifMatch = ctx.DefineLabel(), nextTest = ctx.DefineLabel(); ctx.CopyValue(); ctx.TryCast(serType); ctx.CopyValue(); ctx.BranchIfTrue(ifMatch, true); ctx.DiscardValue(); ctx.Branch(nextTest, false); ctx.MarkLabel(ifMatch); typeser.EmitCallback(ctx, null, callbackType); ctx.Branch(@break, false); ctx.MarkLabel(nextTest); } } ctx.MarkLabel(@break); ctx.DiscardValue(); } } }
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); // stack is [new-value] if (writeValue) { using (Compiler.Local newVal = new Compiler.Local(ctx, property.PropertyType)) { 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 (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) { Compiler.CodeLabel done = ctx.DefineLabel(); if (valueFrom == null) { ctx.CopyValue(); // on the stack Compiler.CodeLabel needToPop = ctx.DefineLabel(); EmitBranchIfDefaultValue(ctx, needToPop); Tail.EmitWrite(ctx, null); ctx.Branch(done, true); ctx.MarkLabel(needToPop); ctx.DiscardValue(); } else { ctx.LoadValue(valueFrom); // variable/parameter EmitBranchIfDefaultValue(ctx, done); Tail.EmitWrite(ctx, valueFrom); } ctx.MarkLabel(done); }
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(); } } }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { Tail.EmitRead(ctx, valueFrom); ctx.CopyValue(); Compiler.CodeLabel @nonEmpty = ctx.DefineLabel(), @end = ctx.DefineLabel(); ctx.LoadValue(typeof(string).GetProperty("Length")); ctx.BranchIfTrue(@nonEmpty, true); ctx.DiscardValue(); ctx.LoadNullRef(); ctx.Branch(@end, true); ctx.MarkLabel(@nonEmpty); ctx.EmitCtor(ctx.MapType(typeof(Uri)), ctx.MapType(typeof(string))); ctx.MarkLabel(@end); }
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) { using (Compiler.Local newVal = new Compiler.Local(ctx, field.FieldType)) { 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); 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 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 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); } }
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(); } }
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 } 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) { 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); } } }
void IProtoTypeSerializer.EmitCallback(Compiler.CompilerContext ctx, Compiler.Local valueFrom, TypeModel.CallbackType callbackType) { bool actuallyHasInheritance = false; if (CanHaveInheritance) { for (int i = 0; i < serializers.Length; i++) { IProtoSerializer ser = serializers[i]; if (ser.ExpectedType != forType && ((IProtoTypeSerializer)ser).HasCallbacks(callbackType)) { actuallyHasInheritance = true; } } } Helpers.DebugAssert(((IProtoTypeSerializer)this).HasCallbacks(callbackType), "Shouldn't be calling this if there is nothing to do"); MethodInfo method = callbacks?[callbackType]; if (method == null && !actuallyHasInheritance) { return; } if (!method.IsStatic) { ctx.LoadAddress(valueFrom, ExpectedType); } EmitInvokeCallback(ctx, method, actuallyHasInheritance, null, forType); if (actuallyHasInheritance) { // Make sure that the object is on the top of the stack. // If the callback is non-static, it's already here from the EmitInvokeCallback call. if (method.IsStatic) { ctx.LoadAddress(valueFrom, ExpectedType); } Compiler.CodeLabel @break = ctx.DefineLabel(); for (int i = 0; i < serializers.Length; i++) { IProtoSerializer ser = serializers[i]; IProtoTypeSerializer typeser; Type serType = ser.ExpectedType; if (serType != forType && (typeser = (IProtoTypeSerializer)ser).HasCallbacks(callbackType)) { Compiler.CodeLabel ifMatch = ctx.DefineLabel(), nextTest = ctx.DefineLabel(); ctx.CopyValue(); ctx.TryCast(serType); ctx.CopyValue(); ctx.BranchIfTrue(ifMatch, true); ctx.DiscardValue(); ctx.Branch(nextTest, false); ctx.MarkLabel(ifMatch); typeser.EmitCallback(ctx, null, callbackType); ctx.Branch(@break, false); ctx.MarkLabel(nextTest); } } ctx.MarkLabel(@break); ctx.DiscardValue(); } }
private static void EmitReadAndAddItem(Compiler.CompilerContext ctx, Compiler.Local list, IProtoSerializer tail, MethodInfo add) { ctx.LoadValue(list); Type itemType = tail.ExpectedType; if (tail.RequiresOldValue) { if (itemType.IsValueType || !tail.ReturnsValue) { // 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 (!tail.ReturnsValue) { 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 (tail.ReturnsValue) { // 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 == typeof(object)) { ctx.CastToObject(itemType); } else { throw new InvalidOperationException("Conflicting item/add type"); } } ctx.EmitCall(add); if (add.ReturnType != typeof(void)) { ctx.DiscardValue(); } }
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(); } } }
} // 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 (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); } } } }