protected override void EmitRead(ProtoBuf.Compiler.CompilerContext ctx, ProtoBuf.Compiler.Local valueFrom) { /* This looks more complex than it is. Look at the non-compiled Read to * see what it is trying to do, but note that it needs to cope with a * few more scenarios. Note that it picks the **most specific** Add, * unlike the runtime version that uses IList when possible. The core * is just a "do {list.Add(readValue())} while {thereIsMore}" * * The complexity is due to: * - value types vs reference types (boxing etc) * - initialization if we need to pass in a value to the tail * - handling whether or not the tail *returns* the value vs updates the input */ bool returnList = ReturnList; using (Compiler.Local list = AppendToCollection ? ctx.GetLocalWithValue(ExpectedType, valueFrom) : new Compiler.Local(ctx, declaredType)) using (Compiler.Local origlist = (returnList && AppendToCollection) ? new Compiler.Local(ctx, ExpectedType) : null) { if (!AppendToCollection) { // always new ctx.LoadNullRef(); ctx.StoreValue(list); } else if (returnList) { // need a copy ctx.LoadValue(list); ctx.StoreValue(origlist); } if (concreteType != null) { ctx.LoadValue(list); Compiler.CodeLabel notNull = ctx.DefineLabel(); ctx.BranchIfTrue(notNull, true); ctx.EmitCtor(concreteType); ctx.StoreValue(list); ctx.MarkLabel(notNull); } EmitReadList(ctx, list, Tail, add, packedWireType); if (returnList) { if (AppendToCollection) { // remember ^^^^ we had a spare copy of the list on the stack; now we'll compare ctx.LoadValue(origlist); ctx.LoadValue(list); // [orig] [new-value] Compiler.CodeLabel sameList = ctx.DefineLabel(), allDone = ctx.DefineLabel(); ctx.BranchIfEqual(sameList, true); ctx.LoadValue(list); ctx.Branch(allDone, true); ctx.MarkLabel(sameList); ctx.LoadNullRef(); ctx.MarkLabel(allDone); } else { ctx.LoadValue(list); } } } }