protected override void EmitWrite(AqlaSerializer.Compiler.CompilerContext ctx, AqlaSerializer.Compiler.Local valueFrom) { var g = ctx.G; using (ctx.StartDebugBlockAuto(this)) using (Compiler.Local value = ctx.GetLocalWithValue(ExpectedType, valueFrom)) using (Compiler.Local t = ctx.Local(typeof(System.Type))) using (Compiler.Local length = ctx.Local(typeof(int))) using (var icol = !_protoCompatibility ? ctx.Local(typeof(ICollection)) : null) { ListHelpers.EmitWrite( ctx.G, value, () => { g.Assign(icol, value.AsOperand.As(icol.Type)); g.Assign(length, (icol.AsOperand != null).Conditional(icol.AsOperand.Property("Count"), -1)); g.If(length.AsOperand > 0); { g.Writer.WriteFieldHeader(ListHelpers.FieldLength, WireType.Variant); g.Writer.WriteInt32(length); } g.End(); if (_writeSubType) { g.Assign(t, value.AsOperand.InvokeGetType()); g.If(_concreteTypeDefault != t.AsOperand); { g.Writer.WriteFieldHeaderBegin(ListHelpers.FieldSubtype); _subTypeHelpers.EmitWrite(g, _metaType, value); } g.End(); } }, null); } }
protected override void EmitRead(AqlaSerializer.Compiler.CompilerContext ctx, AqlaSerializer.Compiler.Local valueFrom) { var g = ctx.G; using (ctx.StartDebugBlockAuto(this)) using (Compiler.Local value = ctx.GetLocalWithValueForEmitRead(this, valueFrom)) using (Compiler.Local result = ctx.Local(_arrayType, true)) using (Compiler.Local reservedTrap = ctx.Local(typeof(int))) using (Compiler.Local list = ctx.Local(ctx.MapType(typeof(List <>)).MakeGenericType(_itemType))) using (Compiler.Local index = ctx.Local(typeof(int))) using (Compiler.Local length = ctx.Local(typeof(int?), true)) using (Compiler.Local oldLen = ctx.Local(typeof(int))) { g.Assign(reservedTrap, -1); _listHelpers.EmitRead( ctx.G, (onSuccess, onFail) => { using (ctx.StartDebugBlockAuto(this, "meta")) { g.If(g.ReaderFunc.TryReadFieldHeader_bool(ListHelpers.FieldLength)); { g.Assign(length, g.ReaderFunc.ReadInt32()); onSuccess(); } g.Else(); { onFail(); } g.End(); } }, () => { using (ctx.StartDebugBlockAuto(this, "prepareInstance")) { g.If(length.AsOperand >= 0); { ctx.MarkDebug("// length read, creating instance"); var lengthValue = length.AsOperand.Property("Value"); g.If(lengthValue > _readLengthLimit); { EmitThrowExceededLengthLimit(g, lengthValue, _readLengthLimit); } g.End(); EmitRead_CreateInstance(g, value, lengthValue, null, oldLen, result); g.Assign(index, oldLen); } g.Else(); { ctx.MarkDebug("// length read, creating list"); g.Assign(reservedTrap, g.ReaderFunc.ReserveNoteObject_int()); g.Assign(list, g.ExpressionFactory.New(list.Type)); } g.End(); } }, v => { using (ctx.StartDebugBlockAuto(this, "add")) { g.If(result.AsOperand != null); { g.Assign(result.AsOperand[index], v); g.Increment(index); } g.Else(); { g.Invoke(list, "Add", v); } g.End(); } } ); g.If(result.AsOperand == null); { ctx.MarkDebug("// result == null, creating"); EmitRead_CreateInstance(g, value, list.AsOperand.Property("Count"), reservedTrap, oldLen, result); g.Invoke(list, "CopyTo", result, oldLen); } g.End(); if (EmitReadReturnsValue) { ctx.LoadValue(result); } else { g.Assign(value, result); } } }
protected override void EmitRead(AqlaSerializer.Compiler.CompilerContext ctx, AqlaSerializer.Compiler.Local valueFrom) { var g = ctx.G; using (ctx.StartDebugBlockAuto(this)) using (Compiler.Local value = ctx.GetLocalWithValueForEmitRead(this, valueFrom)) using (Compiler.Local result = ctx.Local(_arrayType, true)) using (Compiler.Local lengthTemp = ctx.Local(typeof(int))) using (Compiler.Local deepestRank = ctx.Local(typeof(int), true)) using (Compiler.Local totalLength = ctx.Local(typeof(int))) { var lengths = Enumerable.Range(0, _rank).Select(x => g.ctx.Local(typeof(int))).ToArray(); var indexes = Enumerable.Range(0, _rank).Select(x => g.ctx.Local(typeof(int), true)).ToArray(); var indexesOp = indexes.Select(x => (Operand)x).ToArray(); var deepestRankLength = lengths[lengths.Length - 1]; g.Assign(totalLength, 1); _listHelpers.EmitRead( ctx.G, (onSuccess, onFail) => { using (ctx.StartDebugBlockAuto(this, "meta")) { g.If(g.ReaderFunc.TryReadFieldHeader_bool(ListHelpers.FieldLength)); { g.Assign(lengthTemp, g.ReaderFunc.ReadInt32()); g.Switch(deepestRank); { for (int i = 0; i < _rank; i++) { g.Case(i); { g.Assign(lengths[i], lengthTemp); } g.Break(); } g.DefaultCase(); EmitThrowWrongRank(g); } g.End(); g.Increment(deepestRank); g.Assign(totalLength, totalLength.AsOperand * lengthTemp.AsOperand); onSuccess(); } g.Else(); { onFail(); } g.End(); } }, () => { using (ctx.StartDebugBlockAuto(this, "prepareInstance")) { g.If(deepestRank.AsOperand != _rank); { EmitThrowWrongRank(g); } g.End(); //g.Decrement(deepestRank); - not used g.If(totalLength.AsOperand > _readLengthLimit); { ArrayDecorator.EmitThrowExceededLengthLimit(g, totalLength, _readLengthLimit); } g.End(); ctx.MarkDebug("// length read, creating instance"); EmitRead_CreateInstance(g, value, lengths, indexes[0], result); } }, v => { using (ctx.StartDebugBlockAuto(this, "add")) { g.Assign(result.AsOperand[indexesOp], v); var newIndex = indexes[_rank - 1]; g.Increment(newIndex); g.If(newIndex.AsOperand >= deepestRankLength.AsOperand); { // unwrapped loop var breakLabel = g.DefineLabel(); int rankIndex = _rank - 1; while (rankIndex > 0) { g.Assign(indexes[rankIndex], 0); --rankIndex; g.Increment(indexes[rankIndex]); g.If(indexes[rankIndex].AsOperand < lengths[rankIndex].AsOperand); { g.Goto(breakLabel); } g.End(); } g.MarkLabel(breakLabel); } g.End(); } }); foreach (Local local in indexes.Concat(lengths)) { local.Dispose(); } if (EmitReadReturnsValue) { ctx.LoadValue(result); } else { g.Assign(value, result); } } }
protected override void EmitRead(AqlaSerializer.Compiler.CompilerContext ctx, AqlaSerializer.Compiler.Local valueFrom) { var g = ctx.G; using (ctx.StartDebugBlockAuto(this)) using (Compiler.Local value = ctx.GetLocalWithValueForEmitRead(this, valueFrom)) using (Compiler.Local oldValueForSubTypeHelpers = ctx.Local(value.Type)) using (Compiler.Local createdNew = ctx.Local(typeof(bool), true)) { bool asList = IsList && !SuppressIList; // can't call clear? => create new! bool forceNewInstance = !AppendToCollection && !asList; ListHelpers.EmitRead( ctx.G, (onSuccess, onFail) => { using (ctx.StartDebugBlockAuto(this, "readNextMeta")) { if (_metaType != null) { g.If(g.ReaderFunc.TryReadFieldHeader_bool(ListHelpers.FieldSubtype)); { using (ctx.StartDebugBlockAuto(this, "subtype handler")) { g.Assign(oldValueForSubTypeHelpers, forceNewInstance ? null : value.AsOperand); _subTypeHelpers.EmitTryRead( g, oldValueForSubTypeHelpers, _metaType, r => { using (ctx.StartDebugBlockAuto(this, "subtype handler - read")) { if (r != null) { ctx.MarkDebug("// creating list subtype"); r.Serializer.EmitCreateInstance(ctx); ctx.StoreValue(value); g.Assign(createdNew, true); } } }); } onSuccess(); } g.Else(); { onFail(); } g.End(); } else { onFail(); } } } , () => { using (ctx.StartDebugBlockAuto(this, "prepareInstance")) { var createInstanceCondition = value.AsOperand == null; // also create new if should clear existing instance on not lists if (forceNewInstance) { createInstanceCondition = createInstanceCondition || !createdNew.AsOperand; } g.If(createInstanceCondition); { ctx.MarkDebug("// creating new list"); EmitCreateInstance(ctx); ctx.StoreValue(value); g.Reader.NoteObject(value); } g.Else(); { g.If(!createdNew.AsOperand); { g.Reader.NoteObject(value); if (asList && !AppendToCollection) { ctx.MarkDebug("// clearing existing list"); // ReSharper disable once PossibleNullReferenceException g.Invoke(value, "Clear"); } } g.End(); } g.End(); } }, v => { // TODO do null checks without allowing user == operators! using (ctx.StartDebugBlockAuto(this, "add")) { #if DEBUG_COMPILE_2 g.If(v.AsOperand != null); { g.ctx.MarkDebug("adding " + v.AsOperand.InvokeToString()); } g.End(); #endif if (asList) { ctx.MarkDebug("// using Add method"); Operand instance = value; if (_add != null && !Helpers.IsAssignableFrom(_add.DeclaringType, ExpectedType)) { instance = instance.Cast(_add.DeclaringType); // TODO optimize to local } g.Invoke(instance, "Add", v); } else { ctx.MarkDebug("// using add delegate"); ctx.LoadAddress(value, ExpectedType); if (!Helpers.IsAssignableFrom(_add.DeclaringType, ExpectedType)) { ctx.Cast(_add.DeclaringType); } ctx.LoadValue(v); ctx.EmitCall(this._add); } } } ); if (EmitReadReturnsValue) { ctx.MarkDebug("returning list"); ctx.LoadValue(value); } } }