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 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 EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (ctx.StartDebugBlockAuto(this)) { using (Compiler.Local valOrNull = ctx.GetLocalWithValue(_expectedType, valueFrom)) { if (_expectedType.IsValueType) { ctx.LoadAddress(valOrNull, _expectedType); ctx.LoadValue(_expectedType.GetProperty("HasValue")); } else { ctx.LoadValue(valOrNull); } Compiler.CodeLabel done = ctx.DefineLabel(); Compiler.CodeLabel onNull = ctx.DefineLabel(); ctx.BranchIfFalse(onNull, false); // if !=null { if (_expectedType.IsValueType) { ctx.LoadAddress(valOrNull, _expectedType); ctx.EmitCall(_expectedType.GetMethod("GetValueOrDefault", Helpers.EmptyTypes)); } else { ctx.LoadValue(valOrNull); } Tail.EmitWrite(ctx, null); ctx.Branch(done, true); } // else { ctx.MarkLabel(onNull); if (_throwIfNull) { ctx.G.ThrowNullReferenceException(); ctx.G.ForceResetUnreachableState(); } else { ctx.G.Writer.WriteFieldHeaderCancelBegin(); } } ctx.MarkLabel(done); } } }
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { if (getSpecified == null) { Tail.EmitWrite(ctx, valueFrom); return; } using (Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom)) { ctx.LoadAddress(loc, ExpectedType); ctx.EmitCall(getSpecified); Compiler.CodeLabel done = ctx.DefineLabel(); ctx.BranchIfFalse(done, false); Tail.EmitWrite(ctx, loc); ctx.MarkLabel(done); } }
public void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (ctx.StartDebugBlockAuto(this)) { var g = ctx.G; using (Compiler.Local value = ctx.GetLocalWithValue(_ctor.DeclaringType, valueFrom)) using (Compiler.Local token = ctx.Local(typeof(SubItemToken))) { g.Assign(token, g.WriterFunc.StartSubItem(value, _prefixLength)); for (int i = 0; i < _tails.Length; i++) { Type type = GetMemberType(i); ctx.LoadAddress(value, ExpectedType); switch (_members[i].Member.MemberType) { case MemberTypes.Field: ctx.LoadValue((FieldInfo)_members[i].Member); break; case MemberTypes.Property: ctx.LoadValue((PropertyInfo)_members[i].Member); break; } ctx.LoadValue(i + 1); ctx.LoadReaderWriter(); ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod(nameof(ProtoWriter.WriteFieldHeaderBegin))); ctx.WriteNullCheckedTail(type, _tails[i], null, true); } g.Writer.EndSubItem(token); } } }
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, ctx.MapType(typeof(SubItemToken)))) using (Compiler.Local field = new Compiler.Local(ctx, ctx.MapType(typeof(int)))) { ctx.LoadReaderWriter(); ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("StartSubItem")); ctx.StoreValue(token); Compiler.CodeLabel next = ctx.DefineLabel(), processField = ctx.DefineLabel(), end = ctx.DefineLabel(); ctx.MarkLabel(next); ctx.EmitBasicRead("ReadFieldHeader", ctx.MapType(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.LoadReaderWriter(); ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("SkipField")); ctx.Branch(next, true); // process ctx.MarkLabel(processField); if (Tail.RequiresOldValue) { if (expectedType.IsValueType) { 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 (expectedType.IsValueType) { ctx.EmitCtor(expectedType, Tail.ExpectedType); // re-nullable<T> it } ctx.StoreValue(oldValue); ctx.Branch(next, false); // outro ctx.MarkLabel(end); ctx.LoadValue(token); ctx.LoadReaderWriter(); ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("EndSubItem")); ctx.LoadValue(oldValue); // load the old value } }
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (ctx.StartDebugBlockAuto(this)) { Type type = ExpectedType; if (type.IsValueType) { // note that for structs, we've already asserted that a custom ToString // exists; no need to handle the box/callvirt scenario // force it to a variable if needed, so we can take the address using (Compiler.Local loc = ctx.GetLocalWithValue(type, valueFrom)) { ctx.LoadAddress(loc, type); ctx.EmitCall(GetCustomToString(type)); } } else { ctx.LoadValue(valueFrom); if (valueFrom.Type != ctx.MapType(typeof(string))) { ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("ToString")); } } ctx.EmitBasicWrite("WriteString", null); } }
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (ctx.StartDebugBlockAuto(this, _getSpecified?.Name)) { if (_getSpecified == null) { Tail.EmitWrite(ctx, valueFrom); return; } using (Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom)) { ctx.LoadAddress(loc, ExpectedType); ctx.EmitCall(_getSpecified); Compiler.CodeLabel done = ctx.DefineLabel(); Compiler.CodeLabel notSpecified = ctx.DefineLabel(); ctx.BranchIfFalse(notSpecified, false); { Tail.EmitWrite(ctx, loc); } ctx.Branch(done, true); ctx.MarkLabel(notSpecified); { ctx.G.Writer.WriteFieldHeaderCancelBegin(); } 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(); } } }
public void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (Compiler.Local loc = ctx.GetLocalWithValue(ctor.DeclaringType, valueFrom)) { for (int i = 0; i < tails.Length; i++) { Type type = GetMemberType(i); ctx.LoadAddress(loc, ExpectedType); switch (members[i].MemberType) { case MemberTypes.Field: ctx.LoadValue((FieldInfo)members[i]); break; case MemberTypes.Property: ctx.LoadValue((PropertyInfo)members[i]); break; } ctx.WriteNullCheckedTail(type, tails[i], null); } } }
private static MethodBuilder EmitBoxedSerializer(TypeBuilder type, int i, Type valueType, SerializerPair[] methodPairs) { MethodInfo dedicated = methodPairs[i].Deserialize; MethodBuilder boxedSerializer = type.DefineMethod("_" + i, MethodAttributes.Static, CallingConventions.Standard, typeof(object), new Type[] { typeof(object), typeof(ProtoReader) }); Compiler.CompilerContext ctx = new Compiler.CompilerContext(boxedSerializer.GetILGenerator(), true, methodPairs); ctx.LoadValue(Compiler.Local.InputValue); Compiler.CodeLabel @null = ctx.DefineLabel(); ctx.BranchIfFalse(@null, true); ctx.LoadValue(Compiler.Local.InputValue); ctx.CastFromObject(valueType); ctx.LoadReaderWriter(); ctx.EmitCall(dedicated); ctx.CastToObject(valueType); ctx.Return(); ctx.MarkLabel(@null); using (Compiler.Local typedVal = new Compiler.Local(ctx, valueType)) { // create a new valueType ctx.LoadAddress(typedVal, valueType); ctx.EmitCtor(valueType); ctx.LoadValue(typedVal); ctx.LoadReaderWriter(); ctx.EmitCall(dedicated); ctx.CastToObject(valueType); ctx.Return(); } return(boxedSerializer); }
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 EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { ctx.LoadAddress(valueFrom, ExpectedType); ctx.LoadValue(field); ctx.WriteNullCheckedTail(field.FieldType, Tail, null); }
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (ctx.StartDebugBlockAuto(this, _property.Name)) { ctx.LoadAddress(valueFrom, ExpectedType); ctx.LoadValue(_property); Tail.EmitWrite(ctx, null); } }
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); } } } } }
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) { if (setSpecified is null) { Tail.EmitRead(ctx, valueFrom); return; } using Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom); Tail.EmitRead(ctx, loc); ctx.LoadAddress(loc, ExpectedType); ctx.LoadValue(1); // true ctx.EmitCall(setSpecified); }
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 static MethodContext EmitBoxedSerializer(TypeBuilder type, int i, Type valueType, SerializerPair[] methodPairs, RuntimeTypeModel model, Compiler.CompilerContext.ILVersion ilVersion, string assemblyName) { MethodInfo dedicated = methodPairs[i].Deserialize; string name = "_" + i.ToString(); MethodContext methodContext; model.EmitDefineMethod( type, name, MethodAttributes.Static, CallingConventions.Standard, model.MapType(typeof(object)), new[] { new MethodContext.ParameterGenInfo(model.MapType(typeof(object)), "obj", 1), new MethodContext.ParameterGenInfo(model.MapType(typeof(ProtoReader)), "source", 2), }, false, out methodContext); Compiler.CompilerContext ctx = new Compiler.CompilerContext(methodContext, true, false, methodPairs, model, ilVersion, assemblyName, model.MapType(typeof(object))); ctx.LoadValue(ctx.InputValue); Compiler.CodeLabel @null = ctx.DefineLabel(); ctx.BranchIfFalse(@null, true); Type mappedValueType = valueType; ctx.LoadValue(ctx.InputValue); ctx.CastFromObject(mappedValueType); ctx.LoadReaderWriter(); ctx.EmitCall(dedicated); ctx.CastToObject(mappedValueType); ctx.Return(); ctx.MarkLabel(@null); using (Compiler.Local typedVal = new Compiler.Local(ctx, mappedValueType)) { // create a new valueType ctx.LoadAddress(typedVal, mappedValueType); ctx.EmitCtor(mappedValueType); ctx.LoadValue(typedVal); ctx.LoadReaderWriter(); ctx.EmitCall(dedicated); ctx.CastToObject(mappedValueType); ctx.Return(); } return(methodContext); }
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); } }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (ctx.StartDebugBlockAuto(this, _setSpecified?.Name)) { if (_setSpecified == null) { Tail.EmitRead(ctx, valueFrom); return; } using (Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom)) { Tail.EmitRead(ctx, loc); ctx.LoadAddress(loc, ExpectedType); ctx.LoadValue(1); // true ctx.EmitCall(_setSpecified); } } }
public void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using Compiler.Local loc = ctx.GetLocalWithValue(ctor.DeclaringType, valueFrom); for (int i = 0; i < tails.Length; i++) { Type type = GetMemberType(i); ctx.LoadAddress(loc, ExpectedType); if (members[i] is FieldInfo fieldInfo) { ctx.LoadValue(fieldInfo); } else if (members[i] is PropertyInfo propertyInfo) { ctx.LoadValue(propertyInfo); } ctx.WriteNullCheckedTail(type, tails[i], null); } }
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(); } } }
void IRuntimeProtoSerializerNode.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { Type type = ExpectedType; using var loc = ctx.GetLocalWithValue(ExpectedType, valueFrom); ctx.LoadState(); ctx.LoadAddress(loc, type); if (type.IsValueType) { // note that for structs, we've already asserted that a custom ToString // exists; no need to handle the box/callvirt scenario // force it to a variable if needed, so we can take the address ctx.EmitCall(GetCustomToString(type)); } else { ctx.EmitCall(typeof(object).GetMethod(nameof(object.ToString))); } ctx.LoadNullRef(); // map ctx.EmitCall(typeof(ProtoWriter.State).GetMethod(nameof(ProtoWriter.State.WriteString), BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(string), typeof(StringMap) }, null)); }
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(); } }
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 } } } } }
private static MethodBuilder EmitBoxedSerializer(TypeBuilder type, int i, Type valueType, SerializerPair[] methodPairs, TypeModel model, Compiler.CompilerContext.ILVersion ilVersion, string assemblyName) { MethodInfo dedicated = methodPairs[i].Deserialize; MethodBuilder boxedSerializer = type.DefineMethod("_" + i.ToString(), MethodAttributes.Static, CallingConventions.Standard, model.MapType(typeof(object)), new Type[] { model.MapType(typeof(object)), model.MapType(typeof(ProtoReader)) }); Compiler.CompilerContext ctx = new Compiler.CompilerContext(boxedSerializer.GetILGenerator(), true, false, methodPairs, model, ilVersion, assemblyName, model.MapType(typeof(object))); ctx.LoadValue(ctx.InputValue); Compiler.CodeLabel @null = ctx.DefineLabel(); ctx.BranchIfFalse(@null, true); Type mappedValueType = valueType; ctx.LoadValue(ctx.InputValue); ctx.CastFromObject(mappedValueType); ctx.LoadReaderWriter(); ctx.EmitCall(dedicated); ctx.CastToObject(mappedValueType); ctx.Return(); ctx.MarkLabel(@null); using (Compiler.Local typedVal = new Compiler.Local(ctx, mappedValueType)) { // create a new valueType ctx.LoadAddress(typedVal, mappedValueType); ctx.EmitCtor(mappedValueType); ctx.LoadValue(typedVal); ctx.LoadReaderWriter(); ctx.EmitCall(dedicated); ctx.CastToObject(mappedValueType); ctx.Return(); } return boxedSerializer; }
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); } } }
private static MethodBuilder EmitBoxedSerializer(TypeBuilder type, int i, Type valueType, SerializerPair[] methodPairs) { MethodInfo dedicated = methodPairs[i].Deserialize; MethodBuilder boxedSerializer = type.DefineMethod("_" + i, MethodAttributes.Static, CallingConventions.Standard, typeof(object), new Type[] { typeof(object), typeof(ProtoReader) }); Compiler.CompilerContext ctx = new Compiler.CompilerContext(boxedSerializer.GetILGenerator(), true, false, methodPairs); ctx.LoadValue(Compiler.Local.InputValue); Compiler.CodeLabel @null = ctx.DefineLabel(); ctx.BranchIfFalse(@null, true); ctx.LoadValue(Compiler.Local.InputValue); ctx.CastFromObject(valueType); ctx.LoadReaderWriter(); ctx.EmitCall(dedicated); ctx.CastToObject(valueType); ctx.Return(); ctx.MarkLabel(@null); using(Compiler.Local typedVal = new Compiler.Local(ctx, valueType)) { // create a new valueType ctx.LoadAddress(typedVal, valueType); ctx.EmitCtor(valueType); ctx.LoadValue(typedVal); ctx.LoadReaderWriter(); ctx.EmitCall(dedicated); ctx.CastToObject(valueType); ctx.Return(); } return boxedSerializer; }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (ctx.StartDebugBlockAuto(this, _property.Name)) { var g = ctx.G; bool canSet; SanityCheck(ctx.Model, _property, Tail, out canSet, ctx.NonPublic, ctx.AllowInternal(_property), true); using (Compiler.Local loc = ctx.GetLocalWithValueForEmitRead(this, valueFrom)) using (Compiler.Local oldVal = canSet ? new Compiler.Local(ctx, _property.PropertyType) : null) using (Compiler.Local newVal = new Compiler.Local(ctx, _property.PropertyType)) { Compiler.Local valueForTail = null; if (Tail.RequiresOldValue) { ctx.LoadAddress(loc, ExpectedType); ctx.LoadValue(_property); if (!oldVal.IsNullRef()) { if (Tail.RequiresOldValue) // we need it later { ctx.CopyValue(); } ctx.StoreValue(oldVal); } } if (Tail.RequiresOldValue) // !here! <-- we need value again { if (!Tail.EmitReadReturnsValue) { ctx.StoreValue(newVal); valueForTail = newVal; } // otherwise valueForTail = null (leave value on stack) } Tail.EmitRead(ctx, valueForTail); // otherwise newVal was passed to EmitRead so already has necessary data if (Tail.EmitReadReturnsValue) { ctx.StoreValue(newVal); } // check-condition: //(!Tail.RequiresOldValue // always set where can't check oldVal // // and if it's value type or nullable with changed null/not null or ref // || (Helpers.IsValueType(property.PropertyType) && oldVal != null && newVal != null) // || !ReferenceEquals(oldVal, newVal) // )) if (canSet) { bool check = Tail.RequiresOldValue; if (check) { var condition = !g.StaticFactory.InvokeReferenceEquals(oldVal, newVal); if (Helpers.IsValueType(_property.PropertyType)) { condition = (oldVal.AsOperand != null && newVal.AsOperand != null) || condition; } g.If(condition); } ctx.LoadAddress(loc, ExpectedType); ctx.LoadValue(newVal); if (_shadowSetter == null) { ctx.StoreValue(_property); } else { ctx.EmitCall(_shadowSetter); } if (check) { g.End(); } } if (EmitReadReturnsValue) { ctx.LoadValue(loc); } } } }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (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); } } }