internal void ReadNullCheckedTail(Type type, IProtoSerializer tail, Local valueFrom) { Type type2; if (type.IsValueType && ((type2 = Helpers.GetUnderlyingType(type)) != null)) { if (tail.RequiresOldValue) { using (Local local = this.GetLocalWithValue(type, valueFrom)) { this.LoadAddress(local, type); this.EmitCall(type.GetMethod("GetValueOrDefault", Helpers.EmptyTypes)); } } tail.EmitRead(this, null); if (tail.ReturnsValue) { this.EmitCtor(type, new Type[] { type2 }); } } else { tail.EmitRead(this, valueFrom); } }
private void WriteFieldHandler( Compiler.CompilerContext ctx, Compiler.Local loc, Compiler.CodeLabel handler, Compiler.CodeLabel @continue, IProtoSerializer serializer) { ctx.MarkLabel(handler); Type serType = serializer.ExpectedType; if (serType == ExpectedType) { // emit create if null Helpers.DebugAssert(!loc.IsNullRef()); if (!ExpectedType.IsValueType && CanCreateInstance) { Compiler.CodeLabel afterIf = ctx.DefineLabel(); Compiler.CodeLabel ifContent = ctx.DefineLabel(); // if != null && of correct type ctx.LoadValue(loc); ctx.BranchIfFalse(ifContent, false); ctx.LoadValue(loc); ctx.TryCast(ExpectedType); ctx.BranchIfFalse(ifContent, false); ctx.Branch(afterIf, false); { ctx.MarkLabel(ifContent); ((IProtoTypeSerializer)this).EmitCreateInstance(ctx); if (_callbacks != null) { EmitInvokeCallback(ctx, _callbacks.BeforeDeserialize, true, null, ExpectedType); } ctx.StoreValue(loc); } ctx.MarkLabel(afterIf); } serializer.EmitRead(ctx, loc); } else { ctx.LoadValue(loc); if (ExpectedType.IsValueType || !serializer.EmitReadReturnsValue) { ctx.Cast(serType); } else { ctx.TryCast(serType); // default value can be another inheritance branch } serializer.EmitRead(ctx, null); } if (serializer.EmitReadReturnsValue) { // update the variable ctx.StoreValue(loc); } ctx.Branch(@continue, false); // "continue" }
private void WriteFieldHandler( Compiler.CompilerContext ctx, Type expected, Compiler.Local loc, Compiler.CodeLabel handler, Compiler.CodeLabel @continue, IProtoSerializer serializer) { ctx.MarkLabel(handler); Type serType = serializer.ExpectedType; if (serType == forType) { EmitCreateIfNull(ctx, loc); serializer.EmitRead(ctx, loc); } else { //RuntimeTypeModel rtm = (RuntimeTypeModel)ctx.Model; if (((IProtoTypeSerializer)serializer).CanCreateInstance()) { Compiler.CodeLabel allDone = ctx.DefineLabel(); ctx.LoadValue(loc); ctx.BranchIfFalse(allDone, false); // null is always ok ctx.LoadValue(loc); ctx.TryCast(serType); ctx.BranchIfTrue(allDone, false); // not null, but of the correct type // otherwise, need to convert it ctx.LoadReaderWriter(); ctx.LoadValue(loc); ((IProtoTypeSerializer)serializer).EmitCreateInstance(ctx); ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("Merge")); ctx.Cast(expected); ctx.StoreValue(loc); // Merge always returns a value // nothing needs doing ctx.MarkLabel(allDone); } ctx.LoadValue(loc); ctx.Cast(serType); serializer.EmitRead(ctx, null); } if (serializer.ReturnsValue) { // update the variable ctx.StoreValue(loc); } ctx.Branch(@continue, false); // "continue" }
private void WriteFieldHandler(CompilerContext ctx, Type expected, Local loc, CodeLabel handler, CodeLabel @continue, IProtoSerializer serializer) { ctx.MarkLabel(handler); Type expectedType = serializer.ExpectedType; if (expectedType == this.forType) { this.EmitCreateIfNull(ctx, loc); serializer.EmitRead(ctx, loc); } else { RuntimeTypeModel model = (RuntimeTypeModel)ctx.Model; if (((IProtoTypeSerializer)serializer).CanCreateInstance()) { CodeLabel label = ctx.DefineLabel(); ctx.LoadValue(loc); ctx.BranchIfFalse(label, false); ctx.LoadValue(loc); ctx.TryCast(expectedType); ctx.BranchIfTrue(label, false); ctx.LoadReaderWriter(); ctx.LoadValue(loc); ((IProtoTypeSerializer)serializer).EmitCreateInstance(ctx); ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("Merge")); ctx.Cast(expected); ctx.StoreValue(loc); ctx.MarkLabel(label); } ctx.LoadValue(loc); ctx.Cast(expectedType); serializer.EmitRead(ctx, null); } if (serializer.ReturnsValue) { ctx.StoreValue(loc); } ctx.Branch(@continue, false); }
private void WriteFieldHandler( Compiler.CompilerContext ctx, Type expected, Compiler.Local loc, Compiler.CodeLabel handler, Compiler.CodeLabel @continue, IProtoSerializer serializer) { ctx.MarkLabel(handler); if (serializer.ExpectedType == forType) { EmitCreateIfNull(ctx, expected, loc); serializer.EmitRead(ctx, loc); } else { ctx.LoadValue(loc); ctx.Cast(serializer.ExpectedType); serializer.EmitRead(ctx, null); } if (serializer.ReturnsValue) { // update the variable ctx.StoreValue(loc); } ctx.Branch(@continue, false); // "continue" }
/*public static ProtoCallback BuildCallback(IProtoTypeSerializer head) * { * Type type = head.ExpectedType; * CompilerContext ctx = new CompilerContext(type, true, true); * using (Local typedVal = new Local(ctx, type)) * { * ctx.LoadValue(Local.InputValue); * ctx.CastFromObject(type); * ctx.StoreValue(typedVal); * CodeLabel[] jumpTable = new CodeLabel[4]; * for(int i = 0 ; i < jumpTable.Length ; i++) { * jumpTable[i] = ctx.DefineLabel(); * } * ctx.LoadReaderWriter(); * ctx.Switch(jumpTable); * ctx.Return(); * for(int i = 0 ; i < jumpTable.Length ; i++) { * ctx.MarkLabel(jumpTable[i]); * if (head.HasCallbacks((TypeModel.CallbackType)i)) * { * head.EmitCallback(ctx, typedVal, (TypeModel.CallbackType)i); * } * ctx.Return(); * } * } * * ctx.Emit(OpCodes.Ret); * return (ProtoCallback)ctx.method.CreateDelegate( * typeof(ProtoCallback)); * }*/ public static ProtoDeserializer BuildDeserializer(IProtoSerializer head, TypeModel model) { Type type = head.ExpectedType; CompilerContext ctx = new CompilerContext(type, false, true, model); using (Local typedVal = new Local(ctx, type)) { if (!type.IsValueType) { ctx.LoadValue(Local.InputValue); ctx.CastFromObject(type); ctx.StoreValue(typedVal); } else { ctx.LoadValue(Local.InputValue); CodeLabel notNull = ctx.DefineLabel(), endNull = ctx.DefineLabel(); ctx.BranchIfTrue(notNull, true); ctx.LoadAddress(typedVal, type); ctx.EmitCtor(type); ctx.Branch(endNull, true); ctx.MarkLabel(notNull); ctx.LoadValue(Local.InputValue); ctx.CastFromObject(type); ctx.StoreValue(typedVal); ctx.MarkLabel(endNull); } head.EmitRead(ctx, typedVal); if (head.ReturnsValue) { ctx.StoreValue(typedVal); } ctx.LoadValue(typedVal); ctx.CastToObject(type); } ctx.Emit(OpCodes.Ret); return((ProtoDeserializer)ctx.method.CreateDelegate( typeof(ProtoDeserializer))); }
public static ProtoDeserializer BuildDeserializer(IProtoSerializer head, TypeModel model) { Type expectedType = head.ExpectedType; CompilerContext compilerContext = new CompilerContext(expectedType, false, true, model, typeof(object)); using (Local local = new Local(compilerContext, expectedType)) { if (expectedType.IsValueType) { compilerContext.LoadValue(compilerContext.InputValue); CodeLabel codeLabel = compilerContext.DefineLabel(); CodeLabel codeLabel1 = compilerContext.DefineLabel(); compilerContext.BranchIfTrue(codeLabel, true); compilerContext.LoadAddress(local, expectedType); compilerContext.EmitCtor(expectedType); compilerContext.Branch(codeLabel1, true); compilerContext.MarkLabel(codeLabel); compilerContext.LoadValue(compilerContext.InputValue); compilerContext.CastFromObject(expectedType); compilerContext.StoreValue(local); compilerContext.MarkLabel(codeLabel1); } else { compilerContext.LoadValue(compilerContext.InputValue); compilerContext.CastFromObject(expectedType); compilerContext.StoreValue(local); } head.EmitRead(compilerContext, local); if (head.ReturnsValue) { compilerContext.StoreValue(local); } compilerContext.LoadValue(local); compilerContext.CastToObject(expectedType); } compilerContext.Emit(OpCodes.Ret); return((ProtoDeserializer)compilerContext.method.CreateDelegate(typeof(ProtoDeserializer))); }
private static void EmitReadAndAddItem(Compiler.CompilerContext ctx, Compiler.Local list, IProtoSerializer tail, MethodInfo add, bool castListForAdd) { ctx.LoadAddress(list, list.Type); // needs to be the reference in case the list is value-type (static-call) if (castListForAdd) { ctx.Cast(add.DeclaringType); } Type itemType = tail.ExpectedType; bool tailReturnsValue = tail.ReturnsValue; if (tail.RequiresOldValue) { if (itemType.IsValueType || !tailReturnsValue) { // 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 (!tailReturnsValue) { 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 (tailReturnsValue) { // 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 == ctx.MapType(typeof(object))) { ctx.CastToObject(itemType); } else if (Helpers.GetUnderlyingType(addParamType) == itemType) { // list is nullable ConstructorInfo ctor = Helpers.GetConstructor(addParamType, new Type[] { itemType }, false); ctx.EmitCtor(ctor); // the itemType on the stack is now a Nullable<ItemType> } else { throw new InvalidOperationException("Conflicting item/add type"); } } ctx.EmitCall(add); if (add.ReturnType != ctx.MapType(typeof(void))) { ctx.DiscardValue(); } }
private void WriteFieldHandler( Compiler.CompilerContext ctx, Type expected, Compiler.Local loc, Compiler.CodeLabel handler, Compiler.CodeLabel @continue, IProtoSerializer serializer) { ctx.MarkLabel(handler); Type serType = serializer.ExpectedType; if (serType == ExpectedType) { EmitCreateIfNull(ctx, loc); serializer.EmitRead(ctx, loc); } else { //RuntimeTypeModel rtm = (RuntimeTypeModel)ctx.Model; if (((IProtoTypeSerializer)serializer).CanCreateInstance()) { Compiler.CodeLabel allDone = ctx.DefineLabel(); ctx.LoadValue(loc); ctx.BranchIfFalse(allDone, false); // null is always ok ctx.LoadValue(loc); ctx.TryCast(serType); ctx.BranchIfTrue(allDone, false); // not null, but of the correct type // otherwise, need to convert it ctx.LoadReader(false); ctx.LoadValue(loc); ((IProtoTypeSerializer)serializer).EmitCreateInstance(ctx); ctx.EmitCall(typeof(ProtoReader).GetMethod("Merge", new[] { typeof(ProtoReader), typeof(object), typeof(object) })); ctx.Cast(expected); ctx.StoreValue(loc); // Merge always returns a value // nothing needs doing ctx.MarkLabel(allDone); } if (Helpers.IsValueType(serType)) { Compiler.CodeLabel initValue = ctx.DefineLabel(); Compiler.CodeLabel hasValue = ctx.DefineLabel(); using (Compiler.Local emptyValue = new Compiler.Local(ctx, serType)) { ctx.LoadValue(loc); ctx.BranchIfFalse(initValue, false); ctx.LoadValue(loc); ctx.CastFromObject(serType); ctx.Branch(hasValue, false); ctx.MarkLabel(initValue); ctx.InitLocal(serType, emptyValue); ctx.LoadValue(emptyValue); ctx.MarkLabel(hasValue); } } else { ctx.LoadValue(loc); ctx.Cast(serType); } serializer.EmitRead(ctx, null); } if (serializer.ReturnsValue) { // update the variable if (Helpers.IsValueType(serType)) { // but box it first in case of value type ctx.CastToObject(serType); } ctx.StoreValue(loc); } ctx.Branch(@continue, false); // "continue" }
internal void ReadNullCheckedTail(Type type, IProtoSerializer tail, Compiler.Local valueFrom) { #if !FX11 Type underlyingType; if (type.IsValueType && (underlyingType = Helpers.GetUnderlyingType(type)) != null) { if(tail.RequiresOldValue) { // we expect the input value to be in valueFrom; need to unpack it from T? using (Local loc = GetLocalWithValue(type, valueFrom)) { LoadAddress(loc, type); EmitCall(type.GetMethod("GetValueOrDefault", Helpers.EmptyTypes)); } } else { Helpers.DebugAssert(valueFrom == null); // not expecting a valueFrom in this case } tail.EmitRead(this, null); // either unwrapped on the stack or not provided if (tail.ReturnsValue) { // now re-wrap the value EmitCtor(type, underlyingType); } return; } #endif // either a ref-type of a non-nullable struct; treat "as is", even if null // (the type-serializer will handle the null case; it needs to allow null // inputs to perform the correct type of subclass creation) tail.EmitRead(this, valueFrom); }
/*public static ProtoCallback BuildCallback(IProtoTypeSerializer head) { Type type = head.ExpectedType; CompilerContext ctx = new CompilerContext(type, true, true); using (Local typedVal = new Local(ctx, type)) { ctx.LoadValue(Local.InputValue); ctx.CastFromObject(type); ctx.StoreValue(typedVal); CodeLabel[] jumpTable = new CodeLabel[4]; for(int i = 0 ; i < jumpTable.Length ; i++) { jumpTable[i] = ctx.DefineLabel(); } ctx.LoadReaderWriter(); ctx.Switch(jumpTable); ctx.Return(); for(int i = 0 ; i < jumpTable.Length ; i++) { ctx.MarkLabel(jumpTable[i]); if (head.HasCallbacks((TypeModel.CallbackType)i)) { head.EmitCallback(ctx, typedVal, (TypeModel.CallbackType)i); } ctx.Return(); } } ctx.Emit(OpCodes.Ret); return (ProtoCallback)ctx.method.CreateDelegate( typeof(ProtoCallback)); }*/ public static ProtoDeserializer BuildDeserializer(IProtoSerializer head, TypeModel model) { Type type = head.ExpectedType; CompilerContext ctx = new CompilerContext(type, false, true, model, typeof(object)); using (Local typedVal = new Local(ctx, type)) { if (!type.IsValueType) { ctx.LoadValue(ctx.InputValue); ctx.CastFromObject(type); ctx.StoreValue(typedVal); } else { ctx.LoadValue(ctx.InputValue); CodeLabel notNull = ctx.DefineLabel(), endNull = ctx.DefineLabel(); ctx.BranchIfTrue(notNull, true); ctx.LoadAddress(typedVal, type); ctx.EmitCtor(type); ctx.Branch(endNull, true); ctx.MarkLabel(notNull); ctx.LoadValue(ctx.InputValue); ctx.CastFromObject(type); ctx.StoreValue(typedVal); ctx.MarkLabel(endNull); } head.EmitRead(ctx, typedVal); if (head.ReturnsValue) { ctx.StoreValue(typedVal); } ctx.LoadValue(typedVal); ctx.CastToObject(type); } ctx.Emit(OpCodes.Ret); return (ProtoDeserializer)ctx.method.CreateDelegate( typeof(ProtoDeserializer)); }
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(); } }
protected override void EmitRead(CompilerContext ctx, Local valueFrom) { using (Compiler.Local list = AppendToCollection ? ctx.GetLocalWithValue(ExpectedType, valueFrom) : new Compiler.Local(ctx, typeof(TDictionary))) using (Compiler.Local token = new Compiler.Local(ctx, typeof(SubItemToken))) using (Compiler.Local key = new Compiler.Local(ctx, typeof(TKey))) using (Compiler.Local @value = new Compiler.Local(ctx, typeof(TValue))) using (Compiler.Local fieldNumber = new Compiler.Local(ctx, typeof(int))) { if (!AppendToCollection) { // always new ctx.LoadNullRef(); ctx.StoreValue(list); } if (concreteType != null) { ctx.LoadValue(list); Compiler.CodeLabel notNull = ctx.DefineLabel(); ctx.BranchIfTrue(notNull, true); ctx.EmitCtor(concreteType); ctx.StoreValue(list); ctx.MarkLabel(notNull); } var redoFromStart = ctx.DefineLabel(); ctx.MarkLabel(redoFromStart); // key = default(TKey); value = default(TValue); if (typeof(TKey) == typeof(string)) { ctx.LoadValue(""); ctx.StoreValue(key); } else { ctx.InitLocal(typeof(TKey), key); } if (typeof(TValue) == typeof(string)) { ctx.LoadValue(""); ctx.StoreValue(value); } else { ctx.InitLocal(typeof(TValue), @value); } // token = ProtoReader.StartSubItem(reader); ctx.LoadReader(true); ctx.EmitCall(typeof(ProtoReader).GetMethod("StartSubItem", ProtoReader.State.ReaderStateTypeArray)); ctx.StoreValue(token); Compiler.CodeLabel @continue = ctx.DefineLabel(), processField = ctx.DefineLabel(); // while ... ctx.Branch(@continue, false); // switch(fieldNumber) ctx.MarkLabel(processField); ctx.LoadValue(fieldNumber); CodeLabel @default = ctx.DefineLabel(), one = ctx.DefineLabel(), two = ctx.DefineLabel(); ctx.Switch(new[] { @default, one, two }); // zero based, hence explicit 0 // case 0: default: reader.SkipField(); ctx.MarkLabel(@default); ctx.LoadReader(true); ctx.EmitCall(typeof(ProtoReader).GetMethod("SkipField", ProtoReader.State.StateTypeArray)); ctx.Branch(@continue, false); // case 1: key = ... ctx.MarkLabel(one); keyTail.EmitRead(ctx, null); ctx.StoreValue(key); ctx.Branch(@continue, false); // case 2: value = ... ctx.MarkLabel(two); Tail.EmitRead(ctx, Tail.RequiresOldValue ? @value : null); ctx.StoreValue(value); // (fieldNumber = reader.ReadFieldHeader()) > 0 ctx.MarkLabel(@continue); ctx.EmitBasicRead("ReadFieldHeader", typeof(int)); ctx.CopyValue(); ctx.StoreValue(fieldNumber); ctx.LoadValue(0); ctx.BranchIfGreater(processField, false); // ProtoReader.EndSubItem(token, reader); ctx.LoadValue(token); ctx.LoadReader(true); ctx.EmitCall(typeof(ProtoReader).GetMethod("EndSubItem", new[] { typeof(SubItemToken), typeof(ProtoReader), ProtoReader.State.ByRefStateType })); // list[key] = value; ctx.LoadAddress(list, ExpectedType); ctx.LoadValue(key); ctx.LoadValue(@value); ctx.EmitCall(indexerSet); // while reader.TryReadFieldReader(fieldNumber) ctx.LoadReader(true); ctx.LoadValue(this.fieldNumber); ctx.EmitCall(typeof(ProtoReader).GetMethod("TryReadFieldHeader", new[] { ProtoReader.State.ByRefStateType, typeof(int) })); ctx.BranchIfTrue(redoFromStart, false); if (ReturnsValue) { ctx.LoadValue(list); } } }
private static void EmitReadAndAddItem(CompilerContext ctx, Local list, IProtoSerializer tail, MethodInfo add, bool castListForAdd) { ctx.LoadAddress(list, list.Type); if (castListForAdd) { ctx.Cast(add.DeclaringType); } Type expectedType = tail.ExpectedType; bool returnsValue = tail.ReturnsValue; if (!tail.RequiresOldValue) { if (!returnsValue) { throw new InvalidOperationException(); } tail.EmitRead(ctx, null); } else if (expectedType.IsValueType || !returnsValue) { using (Local local = new Local(ctx, expectedType)) { if (!expectedType.IsValueType) { ctx.LoadNullRef(); ctx.StoreValue(local); } else { ctx.LoadAddress(local, expectedType); ctx.EmitCtor(expectedType); } tail.EmitRead(ctx, local); if (!returnsValue) { ctx.LoadValue(local); } } } else { ctx.LoadNullRef(); tail.EmitRead(ctx, null); } Type parameterType = add.GetParameters()[0].ParameterType; if (parameterType != expectedType) { if (parameterType != ctx.MapType(typeof(object))) { if (Helpers.GetUnderlyingType(parameterType) != expectedType) { throw new InvalidOperationException("Conflicting item/add type"); } Type[] typeArray = new Type[] { expectedType }; ctx.EmitCtor(Helpers.GetConstructor(parameterType, typeArray, false)); } else { ctx.CastToObject(expectedType); } } ctx.EmitCall(add); if (add.ReturnType != ctx.MapType(typeof(void))) { ctx.DiscardValue(); } }
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(); } }