public bool TryEmitSerialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, LocalBuilder value, LocalBuilder packer, LocalBuilder contextLocal, DasherContext context) { var type = value.LocalType; var elementType = type.GetGenericArguments().Single(); // read list length var count = ilg.DeclareLocal(typeof(int)); ilg.Emit(OpCodes.Ldloc, value); ilg.Emit(OpCodes.Callvirt, typeof(IReadOnlyCollection <>).MakeGenericType(elementType).GetProperty(nameof(IReadOnlyList <int> .Count)).GetMethod); ilg.Emit(OpCodes.Stloc, count); // write array header ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Ldloc, count); ilg.Emit(OpCodes.Call, Methods.Packer_PackArrayHeader); // begin loop var loopStart = ilg.DefineLabel(); var loopTest = ilg.DefineLabel(); var i = ilg.DeclareLocal(typeof(int)); ilg.Emit(OpCodes.Ldc_I4_0); ilg.Emit(OpCodes.Stloc, i); ilg.Emit(OpCodes.Br, loopTest); ilg.MarkLabel(loopStart); // loop body ilg.Emit(OpCodes.Ldloc, value); ilg.Emit(OpCodes.Ldloc, i); ilg.Emit(OpCodes.Callvirt, type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Single(p => p.Name == "Item" && p.GetIndexParameters().Length == 1).GetMethod); var elementValue = ilg.DeclareLocal(elementType); ilg.Emit(OpCodes.Stloc, elementValue); if (!SerialiserEmitter.TryEmitSerialiseCode(ilg, throwBlocks, errors, elementValue, packer, context, contextLocal)) { errors.Add($"Cannot serialise IReadOnlyList<> element type {elementType}."); return(false); } // loop counter increment ilg.Emit(OpCodes.Ldloc, i); ilg.Emit(OpCodes.Ldc_I4_1); ilg.Emit(OpCodes.Add); ilg.Emit(OpCodes.Stloc, i); // loop test ilg.MarkLabel(loopTest); ilg.Emit(OpCodes.Ldloc, i); ilg.Emit(OpCodes.Ldloc, count); ilg.Emit(OpCodes.Clt); ilg.Emit(OpCodes.Brtrue, loopStart); return(true); }
public bool TryEmitSerialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, LocalBuilder value, LocalBuilder packer, LocalBuilder contextLocal, DasherContext context) { // treat as complex object and recur var props = value.LocalType .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(p => p.CanRead) .ToList(); if (props.Count == 0) { throw new SerialisationException($"Unable to serialise type \"{value.LocalType}\". It has no dedicated type provider, and has no properties for serialisation as a complex type.", value.LocalType); } // write map header ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Ldc_I4, props.Count); ilg.Emit(OpCodes.Call, Methods.Packer_PackMapHeader); var success = true; // write each property's value foreach (var prop in props) { var propValue = ilg.DeclareLocal(prop.PropertyType); // write property name ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Ldstr, prop.Name); ilg.Emit(OpCodes.Call, Methods.Packer_Pack_String); // get property value ilg.Emit(value.LocalType.GetTypeInfo().IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc, value); ilg.Emit(OpCodes.Call, prop.GetMethod); ilg.Emit(OpCodes.Stloc, propValue); success &= SerialiserEmitter.TryEmitSerialiseCode(ilg, throwBlocks, errors, propValue, packer, context, contextLocal); } return(success); }
public bool TryEmitSerialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, LocalBuilder value, LocalBuilder packer, LocalBuilder contextLocal, DasherContext context) { var type = value.LocalType; var valueType = type.GetGenericArguments().Single(); ilg.Emit(OpCodes.Ldloca, value); ilg.Emit(OpCodes.Call, type.GetProperty(nameof(Nullable <int> .HasValue)).GetMethod); var lblNonNull = ilg.DefineLabel(); var lblExit = ilg.DefineLabel(); ilg.Emit(OpCodes.Brtrue_S, lblNonNull); // value is null ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Call, Methods.Packer_PackNull); ilg.Emit(OpCodes.Br, lblExit); ilg.MarkLabel(lblNonNull); // has a value to serialise var nonNullValue = ilg.DeclareLocal(valueType); ilg.Emit(OpCodes.Ldloca, value); ilg.Emit(OpCodes.Call, type.GetProperty(nameof(Nullable <int> .Value)).GetMethod); ilg.Emit(OpCodes.Stloc, nonNullValue); if (!SerialiserEmitter.TryEmitSerialiseCode(ilg, throwBlocks, errors, nonNullValue, packer, context, contextLocal)) { return(false); } ilg.MarkLabel(lblExit); return(true); }
public bool TryEmitSerialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, LocalBuilder value, LocalBuilder packer, LocalBuilder contextLocal, DasherContext context) { var dicType = value.LocalType; var readOnlyCollectionType = dicType.GetInterfaces().Single(t => t.GetTypeInfo().IsGenericType&& t.GetGenericTypeDefinition() == typeof(IReadOnlyCollection <>)); var enumerableType = dicType.GetInterfaces().Single(t => t.GetTypeInfo().IsGenericType&& t.GetGenericTypeDefinition() == typeof(IEnumerable <>)); var pairType = enumerableType.GenericTypeArguments.Single(); var enumeratorType = typeof(IEnumerator <>).MakeGenericType(pairType); var keyType = dicType.GetGenericArguments()[0]; var valueType = dicType.GetGenericArguments()[1]; // load packer (we'll write the size after loading it) ilg.Emit(OpCodes.Ldloc, packer); // read map size ilg.Emit(OpCodes.Ldloc, value); ilg.Emit(OpCodes.Callvirt, readOnlyCollectionType.GetProperty(nameof(IReadOnlyCollection <int> .Count)).GetMethod); // write map header ilg.Emit(OpCodes.Call, Methods.Packer_PackMapHeader); var enumerator = ilg.DeclareLocal(enumeratorType); ilg.Emit(OpCodes.Ldloc, value); ilg.Emit(OpCodes.Callvirt, enumerableType.GetMethod(nameof(IEnumerable <int> .GetEnumerator))); ilg.Emit(OpCodes.Stloc, enumerator); var pairValue = ilg.DeclareLocal(pairType); var keyValue = ilg.DeclareLocal(keyType); var valueValue = ilg.DeclareLocal(valueType); // try ilg.BeginExceptionBlock(); // begin loop var loopStart = ilg.DefineLabel(); var loopTest = ilg.DefineLabel(); ilg.Emit(OpCodes.Br, loopTest); ilg.MarkLabel(loopStart); // loop body ilg.Emit(OpCodes.Ldloc, enumerator); ilg.Emit(OpCodes.Callvirt, enumeratorType.GetProperty(nameof(IEnumerator <int> .Current)).GetMethod); ilg.Emit(OpCodes.Stloc, pairValue); // read key ilg.Emit(OpCodes.Ldloca, pairValue); ilg.Emit(OpCodes.Call, pairType.GetProperty(nameof(KeyValuePair <int, int> .Key)).GetMethod); ilg.Emit(OpCodes.Stloc, keyValue); // pack key if (!SerialiserEmitter.TryEmitSerialiseCode(ilg, throwBlocks, errors, keyValue, packer, context, contextLocal)) { errors.Add($"Cannot serialise IReadOnlyDictionary<> key type {keyType}."); return(false); } // read value ilg.Emit(OpCodes.Ldloca, pairValue); ilg.Emit(OpCodes.Call, pairType.GetProperty(nameof(KeyValuePair <int, int> .Value)).GetMethod); ilg.Emit(OpCodes.Stloc, valueValue); // pack value if (!SerialiserEmitter.TryEmitSerialiseCode(ilg, throwBlocks, errors, valueValue, packer, context, contextLocal)) { errors.Add($"Cannot serialise IReadOnlyDictionary<> value type {valueType}."); return(false); } // progress enumerator & loop test ilg.MarkLabel(loopTest); ilg.Emit(OpCodes.Ldloc, enumerator); ilg.Emit(OpCodes.Callvirt, Methods.IEnumerator_MoveNext); ilg.Emit(OpCodes.Brtrue, loopStart); // finally ilg.BeginFinallyBlock(); // dispose ilg.Emit(OpCodes.Ldloc, enumerator); ilg.Emit(OpCodes.Callvirt, Methods.IDisposable_Dispose); // end try/finally ilg.EndExceptionBlock(); return(true); }
public bool TryEmitSerialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, LocalBuilder value, LocalBuilder packer, LocalBuilder contextLocal, DasherContext context) { // write header ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Ldc_I4_2); ilg.Emit(OpCodes.Call, Methods.Packer_PackArrayHeader); // TODO might be faster if we a generated class having members for use with called 'Union<>.Match' var typeObj = ilg.DeclareLocal(typeof(Type)); ilg.Emit(OpCodes.Ldloc, value); ilg.Emit(OpCodes.Callvirt, value.LocalType.GetProperty(nameof(Union <int, int> .Type)).GetMethod); ilg.Emit(OpCodes.Stloc, typeObj); // write type name ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Ldloc, typeObj); ilg.Emit(OpCodes.Call, Methods.UnionEncoding_GetTypeName); ilg.Emit(OpCodes.Call, Methods.Packer_Pack_String); var success = true; // loop through types within the union, looking for a match var doneLabel = ilg.DefineLabel(); var labelNextType = ilg.DefineLabel(); foreach (var type in value.LocalType.GetGenericArguments()) { ilg.LoadType(type); ilg.Emit(OpCodes.Ldloc, typeObj); ilg.Emit(OpCodes.Call, Methods.Object_Equals_Object_Object); // continue if this type doesn't match the union's values ilg.Emit(OpCodes.Brfalse, labelNextType); // we have a match // get the value var valueObj = ilg.DeclareLocal(type); ilg.Emit(OpCodes.Ldloc, value); ilg.Emit(OpCodes.Callvirt, value.LocalType.GetProperty(nameof(Union <int, int> .Value)).GetMethod); ilg.Emit(type.GetTypeInfo().IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, type); ilg.Emit(OpCodes.Stloc, valueObj); // write value if (!SerialiserEmitter.TryEmitSerialiseCode(ilg, throwBlocks, errors, valueObj, packer, context, contextLocal)) { errors.Add($"Unable to serialise union member type {type}"); success = false; } ilg.Emit(OpCodes.Br, doneLabel); ilg.MarkLabel(labelNextType); labelNextType = ilg.DefineLabel(); } ilg.MarkLabel(labelNextType); throwBlocks.Throw(() => { ilg.Emit(OpCodes.Ldstr, "No match on union type"); ilg.Emit(OpCodes.Newobj, Methods.Exception_Ctor_String); ilg.Emit(OpCodes.Throw); }); ilg.MarkLabel(doneLabel); return(success); }