internal static void EmitReadList(UcAsp.RPC.ProtoBuf.Compiler.CompilerContext ctx, Compiler.Local list, IProtoSerializer tail, MethodInfo add, WireType packedWireType, bool castListForAdd) { using (Compiler.Local fieldNumber = new Compiler.Local(ctx, ctx.MapType(typeof(int)))) { Compiler.CodeLabel readPacked = packedWireType == WireType.None ? new Compiler.CodeLabel() : ctx.DefineLabel(); if (packedWireType != WireType.None) { ctx.LoadReaderWriter(); ctx.LoadValue(typeof(ProtoReader).GetProperty("WireType")); ctx.LoadValue((int)WireType.String); ctx.BranchIfEqual(readPacked, false); } ctx.LoadReaderWriter(); ctx.LoadValue(typeof(ProtoReader).GetProperty("FieldNumber")); ctx.StoreValue(fieldNumber); Compiler.CodeLabel @continue = ctx.DefineLabel(); ctx.MarkLabel(@continue); EmitReadAndAddItem(ctx, list, tail, add, castListForAdd); ctx.LoadReaderWriter(); ctx.LoadValue(fieldNumber); ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("TryReadFieldHeader")); ctx.BranchIfTrue(@continue, false); if (packedWireType != WireType.None) { Compiler.CodeLabel allDone = ctx.DefineLabel(); ctx.Branch(allDone, false); ctx.MarkLabel(readPacked); ctx.LoadReaderWriter(); ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("StartSubItem")); Compiler.CodeLabel testForData = ctx.DefineLabel(), noMoreData = ctx.DefineLabel(); ctx.MarkLabel(testForData); ctx.LoadValue((int)packedWireType); ctx.LoadReaderWriter(); ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("HasSubValue")); ctx.BranchIfFalse(noMoreData, false); EmitReadAndAddItem(ctx, list, tail, add, castListForAdd); ctx.Branch(testForData, false); ctx.MarkLabel(noMoreData); ctx.LoadReaderWriter(); ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("EndSubItem")); ctx.MarkLabel(allDone); } } }
protected override void EmitRead(UcAsp.RPC.ProtoBuf.Compiler.CompilerContext ctx, UcAsp.RPC.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 && !Helpers.IsValueType(ExpectedType)) ? new Compiler.Local(ctx, ExpectedType) : null) { if (!AppendToCollection) { // always new ctx.LoadNullRef(); ctx.StoreValue(list); } else if (returnList && origlist != null) { // 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); } bool castListForAdd = !add.DeclaringType.IsAssignableFrom(declaredType); EmitReadList(ctx, list, Tail, add, packedWireType, castListForAdd); if (returnList) { if (AppendToCollection && origlist != null) { // 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); } } } }