public override Expression VisitReturnValue(ReturnValue returnValue) { // return a default value of the same type as the return value TypeNode returnType = returnValue.Type; ITypeParameter itp = returnType as ITypeParameter; if (itp != null) { Local loc = new Local(returnType); UnaryExpression loca = new UnaryExpression(loc, NodeType.AddressOf, loc.Type.GetReferenceType()); StatementList statements = new StatementList(2); statements.Add(new AssignmentStatement(new AddressDereference(loca, returnType, false, 0), new Literal(null, SystemTypes.Object))); statements.Add(new ExpressionStatement(loc)); return new BlockExpression(new Block(statements), returnType); } if (returnType.IsValueType) return new Literal(0, returnType); return new Literal(null, returnType); }
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 (Helpers.IsValueType(expectedType)) { 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 (Helpers.IsValueType(expectedType)) { 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 } }
public override void VisitAssignmentStatement(AssignmentStatement assignment) { if (assignment.Target is Local && IsResultExpression(assignment.Source)) { exemptResultLocal = (Local) assignment.Target; } base.VisitAssignmentStatement(assignment); }
public override Expression VisitLocal(Local local) { if (HelperMethods.IsClosureType(this.parentType, local.Type)) { // don't copy local as we need to share this closure local return local; } return base.VisitLocal(local); }
internal static void EmitReadList(ProtoBuf.Compiler.CompilerContext ctx, Compiler.Local list, IProtoSerializer tail, MethodInfo add, WireType packedWireType) { using (Compiler.Local fieldNumber = new Compiler.Local(ctx, 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); ctx.LoadReaderWriter(); ctx.LoadValue(fieldNumber); ctx.EmitCall(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(typeof(ProtoReader).GetMethod("StartSubItem")); Compiler.CodeLabel testForData = ctx.DefineLabel(), noMoreData = ctx.DefineLabel(); ctx.MarkLabel(testForData); ctx.LoadValue((int)packedWireType); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoReader).GetMethod("HasSubValue")); ctx.BranchIfFalse(noMoreData, false); EmitReadAndAddItem(ctx, list, tail, add); ctx.Branch(testForData, false); ctx.MarkLabel(noMoreData); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoReader).GetMethod("EndSubItem")); ctx.MarkLabel(allDone); } } }
public override void VisitLocal(Local local) { // if the local is a reference to a closure class, then // don't mark it because it is sure to be used in both // the contracts and the method body if (!IsLocalExempt(local) && Locals[local.UniqueKey] == null) { //Console.Write("{0} ", local.Name.Name); Locals[local.UniqueKey] = local; } base.VisitLocal(local); }
/// <summary> /// check for reused local. /// HACK: ignore VB cached boolean locals used in tests. VB does this seemingly for debuggability. /// These are always assigned before use in the same scope, so we ignore them. /// </summary> /// <param name="local"></param> /// <returns></returns> public override void VisitLocal(Local local) { if (gatherLocals.Locals[local.UniqueKey] != null) { if (local.Name != null && local.Name.Name.StartsWith("VB$CG$")) { // ignore VB compiler generated locals. } else { reUseOfExistingLocal = local; } } base.VisitLocal(local); }
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { Helpers.DebugAssert(valueFrom != null); // don't support stack-head for this using (Compiler.Local converted = new Compiler.Local(ctx, tail.ExpectedType)) // declare/re-use local { ctx.LoadValue(valueFrom); // load primary onto stack ctx.EmitCall(toTail); // static convert op, primary-to-surrogate ctx.StoreValue(converted); // store into surrogate local tail.EmitRead(ctx, converted); // downstream processing against surrogate local ctx.LoadValue(converted); // load from surrogate local ctx.EmitCall(fromTail); // static convert op, surrogate-to-primary ctx.StoreValue(valueFrom); // store back into primary } }
public override void VisitLocal(Local local) { // if the local is a reference to a closure class, then // don't mark it because it is sure to be used in both // the contracts and the method body. // Another exception for this rule is an expression initialization // that also introduces local variable that could be used in Contract.Requires if (!IsLocalExempt(local) && Locals[local.UniqueKey] == null) { //Console.Write("{0} ", local.Name.Name); Locals[local.UniqueKey] = local; } base.VisitLocal(local); }
protected override void EmitWrite(ProtoBuf.Compiler.CompilerContext ctx, ProtoBuf.Compiler.Local valueFrom) { // int i and T[] arr using (Compiler.Local arr = ctx.GetLocalWithValue(arrayType, valueFrom)) using (Compiler.Local i = new ProtoBuf.Compiler.Local(ctx, typeof(int))) { if (packedFieldNumber > 0) { Compiler.CodeLabel allDone = ctx.DefineLabel(); ctx.LoadLength(arr, false); ctx.BranchIfFalse(allDone, false); ctx.LoadValue(packedFieldNumber); ctx.LoadValue((int)WireType.String); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("WriteFieldHeader")); ctx.LoadValue(arr); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("StartSubItem")); using (Compiler.Local token = new Compiler.Local(ctx, typeof(SubItemToken))) { ctx.StoreValue(token); ctx.LoadValue(packedFieldNumber); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("SetPackedField")); EmitWriteArrayLoop(ctx, i, arr); ctx.LoadValue(token); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("EndSubItem")); } ctx.MarkLabel(allDone); } else { EmitWriteArrayLoop(ctx, i, arr); } } }
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); 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); } } } } }
/// <summary> /// Use stricter checking if local has a real name (other than "localXXX") /// </summary> private bool IsLocalExempt(Local local) { if (local == exemptResultLocal) return true; bool strict = false; if (local.Name != null && local.Name.Name != null && !local.Name.Name.StartsWith("local")) { strict = true; } var localType = local.Type; if (localType == null) return true; return HelperMethods.IsCompilerGenerated(localType) || local.Name.Name == "_preConditionHolds" // introduced by extractor itself for legacy pre-conditions || (strict ? (LocalNameIsExempt(local.Name.Name)) : (HelperMethods.IsDelegateType(localType) || HelperMethods.IsTypeParameterType(localType) || localType.IsValueType)); }
public void Dispose() { if (local == null || ctx == null) return; ctx.EndTry(label, false); ctx.BeginFinally(); Type disposableType = ctx.MapType(typeof (IDisposable)); MethodInfo dispose = disposableType.GetMethod("Dispose"); Type type = local.Type; // remember that we've already (in the .ctor) excluded the case // where it *cannot* be disposable if (type.IsValueType) { ctx.LoadAddress(local, type); switch (ctx.MetadataVersion) { case ILVersion.Net1: ctx.LoadValue(local); ctx.CastToObject(type); break; default: #if FX11 throw new NotSupportedException(); #else ctx.Constrain(type); break; #endif } ctx.EmitCall(dispose); } else { Compiler.CodeLabel @null = ctx.DefineLabel(); if (disposableType.IsAssignableFrom(type)) { // *known* to be IDisposable; just needs a null-check ctx.LoadValue(local); ctx.BranchIfFalse(@null, true); ctx.LoadAddress(local, type); } else { // *could* be IDisposable; test via "as" using (Compiler.Local disp = new Compiler.Local(ctx, disposableType)) { ctx.LoadValue(local); ctx.TryCast(disposableType); ctx.CopyValue(); ctx.StoreValue(disp); ctx.BranchIfFalse(@null, true); ctx.LoadAddress(disp, disposableType); } } ctx.EmitCall(dispose); ctx.MarkLabel(@null); } ctx.EndFinally(); this.local = null; this.ctx = null; label = new CodeLabel(); // default }
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 (!Helpers.IsValueType(ExpectedType)) { // value-types always read the old value if (Helpers.IsValueType(type)) { 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 = Helpers.IsValueType(ExpectedType) ? new Compiler.CodeLabel() : ctx.DefineLabel(); if (!Helpers.IsValueType(ExpectedType)) { ctx.LoadAddress(objValue, ExpectedType); ctx.BranchIfFalse(skipOld, false); } for (int i = 0; i < members.Length; i++) { ctx.LoadAddress(objValue, ExpectedType); if (members[i] is FieldInfo) { ctx.LoadValue((FieldInfo)members[i]); } else if (members[i] is PropertyInfo) { ctx.LoadValue((PropertyInfo)members[i]); } ctx.StoreValue(locals[i]); } if (!Helpers.IsValueType(ExpectedType)) 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 (Helpers.IsValueType(locals[i].Type)) { 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; }
public override Expression VisitLocal(Local local) { var result = base.VisitLocal(local); var asLoc = result as Local; if (asLoc != null && RemoveNameForLocals) { asLoc.Name = null; // Don't clash with original local name } return result; }
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 (Helpers.IsValueType(expectedType)) { ctx.LoadAddress(valOrNull, expectedType); ctx.LoadValue(expectedType.GetProperty("HasValue")); } else { ctx.LoadValue(valOrNull); } Compiler.CodeLabel @end = ctx.DefineLabel(); ctx.BranchIfFalse(@end, false); if (Helpers.IsValueType(expectedType)) { 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")); } }
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { Type expected = ExpectedType; using (Compiler.Local loc = ctx.GetLocalWithValue(expected, valueFrom)) { // pre-callbacks EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.BeforeSerialize); Compiler.CodeLabel startFields = ctx.DefineLabel(); // inheritance if (CanHaveInheritance) { for (int i = 0; i < serializers.Length; i++) { IProtoSerializer ser = serializers[i]; if (ser.ExpectedType != forType) { Compiler.CodeLabel ifMatch = ctx.DefineLabel(), nextTest = ctx.DefineLabel(); ctx.LoadValue(loc); ctx.TryCast(ser.ExpectedType); ctx.CopyValue(); ctx.BranchIfTrue(ifMatch, true); ctx.DiscardValue(); ctx.Branch(nextTest, true); ctx.MarkLabel(ifMatch); ser.EmitWrite(ctx, null); ctx.Branch(startFields, false); ctx.MarkLabel(nextTest); } } if (constructType != null && constructType != forType) { using(Compiler.Local actualType = new Compiler.Local(ctx, typeof(Type))) { // would have jumped to "fields" if an expected sub-type, so two options: // a: *exactly* that type, b: an *unexpected* type ctx.LoadValue(loc); ctx.EmitCall(typeof(object).GetMethod("GetType")); ctx.CopyValue(); ctx.StoreValue(actualType); ctx.LoadValue(forType); ctx.BranchIfEqual(startFields, true); ctx.LoadValue(actualType); ctx.LoadValue(constructType); ctx.BranchIfEqual(startFields, true); } } else { // would have jumped to "fields" if an expected sub-type, so two options: // a: *exactly* that type, b: an *unexpected* type ctx.LoadValue(loc); ctx.EmitCall(typeof(object).GetMethod("GetType")); ctx.LoadValue(forType); ctx.BranchIfEqual(startFields, true); } // unexpected, then... note that this *might* be a proxy, which // is handled by ThrowUnexpectedSubtype ctx.LoadValue(forType); ctx.LoadValue(loc); ctx.EmitCall(typeof(object).GetMethod("GetType")); ctx.EmitCall(typeof(TypeModel).GetMethod("ThrowUnexpectedSubtype", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)); } // fields ctx.MarkLabel(startFields); for (int i = 0; i < serializers.Length; i++) { IProtoSerializer ser = serializers[i]; if (ser.ExpectedType == forType) ser.EmitWrite(ctx, loc); } // extension data if (isExtensible) { ctx.LoadValue(loc); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("AppendExtensionData")); } // post-callbacks EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.AfterSerialize); } }
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { TypeCode typeCode = GetTypeCode(); if (map == null) { ctx.EmitBasicRead("ReadInt32", typeof(int)); ctx.ConvertFromInt32(typeCode); } else { int[] wireValues = new int[map.Length]; object[] values = new object[map.Length]; for (int i = 0; i < map.Length; i++) { wireValues[i] = map[i].WireValue; values[i] = map[i].Value; } using (Compiler.Local result = new Compiler.Local(ctx, ExpectedType)) using (Compiler.Local wireValue = new Compiler.Local(ctx, typeof(int))) { ctx.EmitBasicRead("ReadInt32", typeof(int)); ctx.StoreValue(wireValue); Compiler.CodeLabel @continue = ctx.DefineLabel(); foreach (BasicList.Group group in BasicList.GetContiguousGroups(wireValues, values)) { Compiler.CodeLabel tryNextGroup = ctx.DefineLabel(); int groupItemCount = group.Items.Count; if (groupItemCount == 1) { // discreet group; use an equality test ctx.LoadValue(wireValue); ctx.LoadValue(group.First); Compiler.CodeLabel processThisValue = ctx.DefineLabel(); ctx.BranchIfEqual(processThisValue, true); ctx.Branch(tryNextGroup, false); WriteEnumValue(ctx, typeCode, processThisValue, @continue, group.Items[0], @result); } else { // implement as a jump-table-based switch ctx.LoadValue(wireValue); ctx.LoadValue(group.First); ctx.Subtract(); // jump-tables are zero-based Compiler.CodeLabel[] jmp = new Compiler.CodeLabel[groupItemCount]; for (int i = 0; i < groupItemCount; i++) { jmp[i] = ctx.DefineLabel(); } ctx.Switch(jmp); // write the default... ctx.Branch(tryNextGroup, false); for (int i = 0; i < groupItemCount; i++) { WriteEnumValue(ctx, typeCode, jmp[i], @continue, group.Items[i], @result); } } ctx.MarkLabel(tryNextGroup); } // throw source.CreateEnumException(ExpectedType, wireValue); ctx.LoadReaderWriter(); ctx.LoadValue(ExpectedType); ctx.LoadValue(wireValue); ctx.EmitCall(typeof(ProtoReader).GetMethod("ThrowEnumException")); ctx.MarkLabel(@continue); ctx.LoadValue(result); } } }
private static void EmitReadAndAddItem(Compiler.CompilerContext ctx, Compiler.Local list, IProtoSerializer tail, MethodInfo add) { ctx.LoadValue(list); Type itemType = tail.ExpectedType; if (tail.RequiresOldValue) { if (itemType.IsValueType || !tail.ReturnsValue) { // 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 (!tail.ReturnsValue) { 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 (tail.ReturnsValue) { // 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 == typeof(object)) { ctx.CastToObject(itemType); } else { throw new InvalidOperationException("Conflicting item/add type"); } } ctx.EmitCall(add); if (add.ReturnType != typeof(void)) { ctx.DiscardValue(); } }
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; }
public virtual Expression VisitLocal(Local local) { if (local == null) return null; local.Type = this.VisitTypeReference(local.Type); LocalBinding lb = local as LocalBinding; if (lb != null) { Local loc = this.VisitLocal(lb.BoundLocal) as Local; if (loc != null) lb.BoundLocal = loc; } return local; }
protected override void EmitWrite(ProtoBuf.Compiler.CompilerContext ctx, ProtoBuf.Compiler.Local valueFrom) { if (arrayType.GetArrayRank() > 1) { return; } // int i and T[] arr using (Compiler.Local arr = ctx.GetLocalWithValue(arrayType, valueFrom)) using (Compiler.Local len = new Compiler.Local(ctx, typeof(int))) using (Compiler.Local i = new ProtoBuf.Compiler.Local(ctx, typeof(int))) using (Compiler.Local writeObject = new Compiler.Local(ctx, typeof(bool))) { // int len = arr.Count; ctx.LoadValue(arr); ctx.LoadValue(arrayType.GetProperty("Length")); ctx.StoreValue(len); // writeObject = true; ctx.LoadValue(true); ctx.StoreValue(writeObject); bool writePacked = (options & OPTIONS_WritePacked) != 0; using (Compiler.Local token = writePacked ? new Compiler.Local(ctx, typeof(SubItemToken)) : null) { if (writePacked) { ctx.LoadValue(fieldNumber); ctx.LoadValue((int)WireType.String); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("WriteFieldHeader")); ctx.LoadValue(arr); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("StartSubItem")); ctx.StoreValue(token); ctx.LoadValue(fieldNumber); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("SetPackedField")); } else { ctx.LoadValue(fieldNumber); ctx.LoadValue((int)WireType.Variant); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("WriteFieldHeader")); if (AsReference) { using (Compiler.Local existing = new Compiler.Local(ctx, typeof(bool))) using (Compiler.Local objectKey = new Compiler.Local(ctx, typeof(int))) { //int objectKey = dest.NetCache.AddObjectKey(value, out existing); ctx.LoadReaderWriter(); ctx.LoadValue(typeof(ProtoWriter).GetProperty("NetCache")); //dest.NetCache ctx.LoadValue(arr); ctx.LoadAddress(existing, typeof(bool)); ctx.EmitCall(typeof(NetObjectCache).GetMethod("AddObjectKey", new Type[] { typeof(object), typeof(bool).MakeByRefType() })); ctx.StoreValue(objectKey); //writeObject = !existing; ctx.LoadValue(existing); Compiler.CodeLabel @writeBoolean = ctx.DefineLabel(); Compiler.CodeLabel @trueCase = ctx.DefineLabel(); ctx.BranchIfTrue(@trueCase, true); ctx.LoadValue(true); ctx.Branch(@writeBoolean, true); ctx.MarkLabel(@trueCase); ctx.LoadValue(false); ctx.MarkLabel(@writeBoolean); ctx.StoreValue(writeObject); //ProtoWriter.WriteUInt32(existing ? (uint)objectKey : 0, dest); using (Compiler.Local valueToWrite = new Compiler.Local(ctx, typeof(uint))) { ctx.LoadValue(0); ctx.StoreValue(valueToWrite); Compiler.CodeLabel @endValueWrite = ctx.DefineLabel(); ctx.LoadValue(existing); ctx.BranchIfFalse(@endValueWrite, true); ctx.LoadValue(objectKey); ctx.CastToObject(typeof(uint)); ctx.StoreValue(valueToWrite); ctx.MarkLabel(@endValueWrite); ctx.LoadValue(valueToWrite); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("WriteUInt32")); } } } else { // bool isEmpty = len == 0; // ProtoWriter.WriteBoolean(isEmpty, dest); ctx.LoadValue(len); ctx.LoadValue(0); ctx.TestEqual(); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("WriteBoolean")); } } Compiler.CodeLabel @endWrite = ctx.DefineLabel(); ctx.LoadValue(writeObject); ctx.BranchIfFalse(@endWrite, false); EmitWriteArrayLoop(ctx, i, arr); ctx.MarkLabel(@endWrite); if (writePacked) { ctx.LoadValue(token); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("EndSubItem")); } } } }
private void EmitReadArray(Compiler.CompilerContext ctx, Compiler.Local list, Compiler.Local oldArr, Compiler.Local newArr, IProtoSerializer tail, MethodInfo add, Type listType) { Compiler.CodeLabel readPacked = packedWireType == WireType.None ? new Compiler.CodeLabel() : ctx.DefineLabel(); Compiler.CodeLabel @end = ctx.DefineLabel(); if (packedWireType != WireType.None) { ctx.LoadReaderWriter(); ctx.LoadValue(typeof(ProtoReader).GetProperty("WireType")); ctx.LoadValue((int)WireType.String); ctx.BranchIfEqual(readPacked, false); } using (Compiler.Local fieldNumber = new Compiler.Local(ctx, typeof(int))) { // int fieldNumber = source.FieldNumber; ctx.LoadReaderWriter(); ctx.LoadValue(typeof (ProtoReader).GetProperty("FieldNumber")); ctx.StoreValue(fieldNumber); using (Compiler.Local readObject = new Compiler.Local(ctx, typeof (bool))) using (Compiler.Local arrayKey = new Compiler.Local(ctx, typeof (int))) { // bool readObject = true; ctx.LoadValue(true); ctx.StoreValue(readObject); // value = value ?? Array.CreateInstance(itemType, 0); Compiler.CodeLabel @endCreateInstance = ctx.DefineLabel(); ctx.LoadValue(oldArr); ctx.LoadNullRef(); ctx.TestEqual(); ctx.BranchIfFalse(@endCreateInstance, true); ctx.LoadValue(itemType); ctx.LoadValue(0); ctx.EmitCall(typeof(Array).GetMethod("CreateInstance", new Type[] { typeof(Type), typeof(int) })); ctx.StoreValue(oldArr); ctx.MarkLabel(@endCreateInstance); if (AsReference) { using (Compiler.Local objectKey = new Compiler.Local(ctx, typeof(int))) { //int objectKey = source.ReadInt32(); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoReader).GetMethod("ReadInt32")); ctx.StoreValue(objectKey); Compiler.CodeLabel @ifNotThere = ctx.DefineLabel(); Compiler.CodeLabel @endIf = ctx.DefineLabel(); //if (objectKey > 0) ctx.LoadValue(objectKey); ctx.LoadValue(0); ctx.BranchIfLessOrEqual(@ifNotThere, true); // value = source.NetCache.GetKeyedObject(objectKey); ctx.LoadReaderWriter(); ctx.LoadValue(typeof(ProtoReader).GetProperty("NetCache")); ctx.LoadValue(objectKey); ctx.EmitCall(typeof(NetObjectCache).GetMethod("GetKeyedObject")); ctx.StoreValue(oldArr); //readObject = false; ctx.LoadValue(false); ctx.StoreValue(readObject); //} ctx.Branch(@endIf, true); //else ctx.MarkLabel(@ifNotThere); //bool dummy; //arrayKey = source.NetCache.AddObjectKey(value, out dummy); using (Compiler.Local dummy = new Compiler.Local(ctx, typeof(bool))) { ctx.LoadReaderWriter(); ctx.LoadValue(typeof(ProtoReader).GetProperty("NetCache")); ctx.LoadValue(oldArr); ctx.LoadAddress(dummy, typeof(bool)); ctx.EmitCall(typeof(NetObjectCache).GetMethod("AddObjectKey", new Type[] { typeof(object), typeof(bool).MakeByRefType() })); ctx.StoreValue(arrayKey); } ctx.MarkLabel(@endIf); } } else { using (Compiler.Local readValue = new Compiler.Local(ctx, typeof(int))) { ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoReader).GetMethod("ReadInt32")); ctx.StoreValue(readValue); Compiler.CodeLabel @codeContinues = ctx.DefineLabel(); ctx.LoadValue(readValue); ctx.LoadValue(0); ctx.BranchIfEqual(@codeContinues, true); ctx.LoadValue(oldArr); ctx.Branch(@end, false); ctx.MarkLabel(@codeContinues); } } /* if (readObject) { while (source.TryReadFieldHeader(field)) { list.Add(Tail.Read(null, source)); } */ Compiler.CodeLabel @finishRead = ctx.DefineLabel(); ctx.LoadValue(readObject); ctx.BranchIfFalse(@finishRead, false); Compiler.CodeLabel @beginWhileLoop = ctx.DefineLabel(); Compiler.CodeLabel @endWhileLoop = ctx.DefineLabel(); ctx.MarkLabel(@beginWhileLoop); ctx.LoadReaderWriter(); ctx.LoadValue(fieldNumber); ctx.EmitCall(typeof (ProtoReader).GetMethod("TryReadFieldHeader")); ctx.BranchIfFalse(@endWhileLoop, false); ListDecorator.EmitReadAndAddItem(ctx, list, tail, add, null); ctx.Branch(@beginWhileLoop, false); ctx.MarkLabel(@endWhileLoop); // Array newArray = Array.CreateInstance(itemType, list.Count); ctx.LoadValue(itemType); ctx.LoadValue(list); ctx.LoadValue(listType.GetProperty("Count")); ctx.EmitCall(typeof(Array).GetMethod("CreateInstance", new Type[] { typeof(Type), typeof(int) })); ctx.StoreValue(newArr); // list.CopyTo(newArray, 0); ctx.LoadValue(list); ctx.LoadValue(newArr); ctx.LoadValue(0); ctx.EmitCall(listType.GetMethod("CopyTo", new Type[] { ExpectedType, typeof(int) })); if (AsReference) { //source.NetCache.UpdateKeyedObject(arrayKey, value, newArray); ctx.LoadReaderWriter(); ctx.LoadValue(typeof(ProtoReader).GetProperty("NetCache")); ctx.LoadValue(arrayKey); ctx.LoadValue(oldArr); ctx.LoadValue(newArr); ctx.EmitCall(typeof(NetObjectCache).GetMethod("UpdateKeyedObject")); } //value = newArray; ctx.LoadValue(newArr); ctx.StoreValue(oldArr); ctx.MarkLabel(@finishRead); ctx.LoadValue(oldArr); ctx.MarkLabel(@end); } } if (packedWireType != WireType.None) { Compiler.CodeLabel allDone = ctx.DefineLabel(); ctx.Branch(allDone, false); ctx.MarkLabel(readPacked); ctx.LoadReaderWriter(); ctx.EmitCall(typeof (ProtoReader).GetMethod("StartSubItem")); Compiler.CodeLabel testForData = ctx.DefineLabel(), noMoreData = ctx.DefineLabel(); ctx.MarkLabel(testForData); ctx.LoadValue((int) packedWireType); ctx.LoadReaderWriter(); ctx.EmitCall(typeof (ProtoReader).GetMethod("HasSubValue")); ctx.BranchIfFalse(noMoreData, false); ListDecorator.EmitReadAndAddItem(ctx, list, tail, add, null); ctx.Branch(testForData, false); ctx.MarkLabel(noMoreData); ctx.LoadReaderWriter(); ctx.EmitCall(typeof (ProtoReader).GetMethod("EndSubItem")); EmitReadEndPacked(ctx, list, oldArr, newArr, listType); ctx.MarkLabel(allDone); } }
protected override void EmitRead(ProtoBuf.Compiler.CompilerContext ctx, ProtoBuf.Compiler.Local valueFrom) { if (arrayType.GetArrayRank() > 1) { return; } Type listType; #if NO_GENERICS listType = typeof(BasicList); #else listType = typeof (System.Collections.Generic.List<>).MakeGenericType(itemType); #endif if (nested) { ctx.LoadReaderWriter(); ctx.EmitCall(typeof (ProtoReader).GetMethod("ReadFieldHeader")); ctx.DiscardValue(); } using (Compiler.Local oldArr = AppendToCollection ? ctx.GetLocalWithValue(ExpectedType, valueFrom) : null) using (Compiler.Local newArr = new Compiler.Local(ctx, ExpectedType)) using (Compiler.Local list = new Compiler.Local(ctx, listType)) { ctx.EmitCtor(listType); ctx.StoreValue(list); EmitReadArray(ctx, list, oldArr, newArr, Tail, listType.GetMethod("Add"), listType); } }
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"); } 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 (!propertyType.IsValueType) { // 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 (!propertyType.IsValueType) { 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(ProtoBuf.Compiler.CompilerContext ctx, ProtoBuf.Compiler.Local valueFrom) { Type listType; #if NO_GENERICS listType = typeof(BasicList); #else listType = ctx.MapType(typeof(System.Collections.Generic.List<>)).MakeGenericType(itemType); #endif Type expected = ExpectedType; using (Compiler.Local oldArr = AppendToCollection ? ctx.GetLocalWithValue(expected, valueFrom) : null) using (Compiler.Local newArr = new Compiler.Local(ctx, expected)) using (Compiler.Local list = new Compiler.Local(ctx, listType)) { ctx.EmitCtor(listType); ctx.StoreValue(list); ListDecorator.EmitReadList(ctx, list, Tail, listType.GetMethod("Add"), packedWireType, false); // leave this "using" here, as it can share the "FieldNumber" local with EmitReadList using(Compiler.Local oldLen = AppendToCollection ? new ProtoBuf.Compiler.Local(ctx, ctx.MapType(typeof(int))) : null) { Type[] copyToArrayInt32Args = new Type[] { ctx.MapType(typeof(Array)), ctx.MapType(typeof(int)) }; if (AppendToCollection) { ctx.LoadLength(oldArr, true); ctx.CopyValue(); ctx.StoreValue(oldLen); ctx.LoadAddress(list, listType); ctx.LoadValue(listType.GetProperty("Count")); ctx.Add(); ctx.CreateArray(itemType, null); // length is on the stack ctx.StoreValue(newArr); ctx.LoadValue(oldLen); Compiler.CodeLabel nothingToCopy = ctx.DefineLabel(); ctx.BranchIfFalse(nothingToCopy, true); ctx.LoadValue(oldArr); ctx.LoadValue(newArr); ctx.LoadValue(0); // index in target ctx.EmitCall(expected.GetMethod("CopyTo", copyToArrayInt32Args)); ctx.MarkLabel(nothingToCopy); ctx.LoadValue(list); ctx.LoadValue(newArr); ctx.LoadValue(oldLen); } else { ctx.LoadAddress(list, listType); ctx.LoadValue(listType.GetProperty("Count")); ctx.CreateArray(itemType, null); ctx.StoreValue(newArr); ctx.LoadAddress(list, listType); ctx.LoadValue(newArr); ctx.LoadValue(0); } copyToArrayInt32Args[0] = expected; // // prefer: CopyTo(T[], int) MethodInfo copyTo = listType.GetMethod("CopyTo", copyToArrayInt32Args); if (copyTo == null) { // fallback: CopyTo(Array, int) copyToArrayInt32Args[1] = ctx.MapType(typeof(Array)); copyTo = listType.GetMethod("CopyTo", copyToArrayInt32Args); } ctx.EmitCall(copyTo); } ctx.LoadValue(newArr); } }
protected override void EmitWrite(ProtoBuf.Compiler.CompilerContext ctx, ProtoBuf.Compiler.Local valueFrom) { using (Compiler.Local list = ctx.GetLocalWithValue(ExpectedType, valueFrom)) { 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; bool writePacked = WritePacked; using (Compiler.Local iter = new Compiler.Local(ctx, enumeratorType)) using (Compiler.Local token = writePacked ? new Compiler.Local(ctx, typeof(SubItemToken)) : null) { if (writePacked) { ctx.LoadValue(fieldNumber); ctx.LoadValue((int)WireType.String); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("WriteFieldHeader")); ctx.LoadValue(list); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("StartSubItem")); ctx.StoreValue(token); ctx.LoadValue(fieldNumber); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("SetPackedField")); } ctx.LoadAddress(list, 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(iter, enumeratorType); ctx.EmitCall(current); Type itemType = Tail.ExpectedType; if (itemType != typeof(object) && current.ReturnType == typeof(object)) { ctx.CastFromObject(itemType); } Tail.EmitWrite(ctx, null); ctx.MarkLabel(@next); ctx.LoadAddress(iter, enumeratorType); ctx.EmitCall(moveNext); ctx.BranchIfTrue(body, false); } if (writePacked) { ctx.LoadValue(token); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoWriter).GetMethod("EndSubItem")); } } } }
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(!ExpectedType.IsValueType) { ctx.LoadValue(oldList); ctx.BranchIfFalse(done, false); // old value null; nothing to add } PropertyInfo prop = Helpers.GetProperty(ExpectedType, "Length", false); if(prop == null) prop = Helpers.GetProperty(ExpectedType, "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); } } }
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { Type expected = ExpectedType; Helpers.DebugAssert(valueFrom != null); using (Compiler.Local loc = ctx.GetLocalWithValue(expected, valueFrom)) using (Compiler.Local fieldNumber = new Compiler.Local(ctx, typeof(int))) { // pre-callbacks if (HasCallbacks(TypeModel.CallbackType.BeforeDeserialize)) { Compiler.CodeLabel callbacksDone = ctx.DefineLabel(); ctx.LoadValue(loc); ctx.BranchIfFalse(callbacksDone, false); EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.BeforeDeserialize); ctx.MarkLabel(callbacksDone); } Compiler.CodeLabel @continue = ctx.DefineLabel(), processField = ctx.DefineLabel(); ctx.Branch(@continue, false); ctx.MarkLabel(processField); foreach (BasicList.Group group in BasicList.GetContiguousGroups(fieldNumbers, serializers)) { Compiler.CodeLabel tryNextField = ctx.DefineLabel(); int groupItemCount = group.Items.Count; if (groupItemCount == 1) { // discreet group; use an equality test ctx.LoadValue(fieldNumber); ctx.LoadValue(group.First); Compiler.CodeLabel processThisField = ctx.DefineLabel(); ctx.BranchIfEqual(processThisField, true); ctx.Branch(tryNextField, false); WriteFieldHandler(ctx, expected, loc, processThisField, @continue, (IProtoSerializer)group.Items[0]); } else { // implement as a jump-table-based switch ctx.LoadValue(fieldNumber); ctx.LoadValue(group.First); ctx.Subtract(); // jump-tables are zero-based Compiler.CodeLabel[] jmp = new Compiler.CodeLabel[groupItemCount]; for (int i = 0; i < groupItemCount; i++) { jmp[i] = ctx.DefineLabel(); } ctx.Switch(jmp); // write the default... ctx.Branch(tryNextField, false); for (int i = 0; i < groupItemCount; i++) { WriteFieldHandler(ctx, expected, loc, jmp[i], @continue, (IProtoSerializer)group.Items[i]); } } ctx.MarkLabel(tryNextField); } EmitCreateIfNull(ctx, expected, loc); ctx.LoadReaderWriter(); if (isExtensible) { ctx.LoadValue(loc); ctx.EmitCall(typeof(ProtoReader).GetMethod("AppendExtensionData")); } else { ctx.EmitCall(typeof(ProtoReader).GetMethod("SkipField")); } ctx.MarkLabel(@continue); ctx.EmitBasicRead("ReadFieldHeader", typeof(int)); ctx.CopyValue(); ctx.StoreValue(fieldNumber); ctx.LoadValue(0); ctx.BranchIfGreater(processField, false); EmitCreateIfNull(ctx, expected, loc); // post-callbacks EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.AfterDeserialize); } }