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); } } } }
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 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 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); } } }