public void IsValidTopLevelType() { var context = new DasherContext(); Assert.False(context.IsValidTopLevelType(typeof(string))); Assert.False(context.IsValidTopLevelType(typeof(int))); Assert.False(context.IsValidTopLevelType(typeof(int?))); Assert.False(context.IsValidTopLevelType(typeof(Tuple <int, float>))); Assert.False(context.IsValidTopLevelType(typeof(TimeSpan))); Assert.False(context.IsValidTopLevelType(typeof(DateTime))); Assert.False(context.IsValidTopLevelType(typeof(Guid))); Assert.False(context.IsValidTopLevelType(typeof(Version))); Assert.False(context.IsValidTopLevelType(typeof(Invalid1))); Assert.False(context.IsValidTopLevelType(typeof(decimal))); Assert.False(context.IsValidTopLevelType(typeof(Union <int, string>))); Assert.True(context.IsValidTopLevelType(typeof(Valid1))); Assert.True(context.IsValidTopLevelType(typeof(Valid2))); Assert.True(context.IsValidTopLevelType(typeof(Union <Valid1, Valid2>))); Assert.True(context.IsValidTopLevelType(typeof(Empty))); Assert.True(context.IsValidTopLevelType(typeof(Union <Empty, Valid1>))); Assert.True(context.IsValidTopLevelType(typeof(Valid2?))); }
public bool TryEmitDeserialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, LocalBuilder contextLocal, DasherContext context, UnexpectedFieldBehaviour unexpectedFieldBehaviour) { var elementType = value.LocalType.GetGenericArguments().Single(); // read list length var count = ilg.DeclareLocal(typeof(int)); ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Ldloca, count); ilg.Emit(OpCodes.Call, Methods.Unpacker_TryReadArrayLength); // verify read correctly throwBlocks.ThrowIfFalse(() => { ilg.Emit(OpCodes.Ldstr, "Expecting collection data to be encoded as an array"); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); var array = ilg.DeclareLocal(elementType.MakeArrayType()); var nonZeroLength = ilg.DefineLabel(); var loopStart = ilg.DefineLabel(); var loopTest = ilg.DefineLabel(); var loopEnd = ilg.DefineLabel(); // if zero-length, return singleton empty array ilg.Emit(OpCodes.Ldloc, count); ilg.Emit(OpCodes.Brtrue, nonZeroLength); ilg.Emit(OpCodes.Call, Methods.EmptyArrayInstanceGetter(elementType)); ilg.Emit(OpCodes.Stloc, array); ilg.Emit(OpCodes.Br, loopEnd); ilg.MarkLabel(nonZeroLength); // create an array to store values ilg.Emit(OpCodes.Ldloc, count); ilg.Emit(OpCodes.Newarr, elementType); ilg.Emit(OpCodes.Stloc, array); // begin loop 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 var element = ilg.DeclareLocal(elementType); if (!DeserialiserEmitter.TryEmitDeserialiseCode(ilg, throwBlocks, errors, name, targetType, element, unpacker, context, contextLocal, unexpectedFieldBehaviour)) { errors.Add($"Unable to deserialise IReadOnlyList<> element type {elementType} from MsgPack data."); return(false); } ilg.Emit(OpCodes.Ldloc, array); ilg.Emit(OpCodes.Ldloc, i); ilg.Emit(OpCodes.Ldloc, element); ilg.Emit(OpCodes.Stelem, elementType); // 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); // after loop ilg.MarkLabel(loopEnd); ilg.Emit(OpCodes.Ldloc, array); ilg.Emit(OpCodes.Stloc, value); return(true); }
public bool TryEmitSerialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, LocalBuilder value, LocalBuilder packer, LocalBuilder contextLocal, DasherContext context) { ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Ldloc, value); ilg.Emit(OpCodes.Call, Methods.Packer_Pack_ByteArraySegment); return(true); }
public void Deserialise(ILGenerator ilg, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, LocalBuilder contextLocal, DasherContext context, UnexpectedFieldBehaviour unexpectedFieldBehaviour) { // Read value as a long var ticks = ilg.DeclareLocal(typeof(long)); ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Ldloca, ticks); ilg.Emit(OpCodes.Call, typeof(Unpacker).GetMethod(nameof(Unpacker.TryReadInt64))); // If the unpacker method failed (returned false), throw var lbl = ilg.DefineLabel(); ilg.Emit(OpCodes.Brtrue, lbl); { ilg.Emit(OpCodes.Ldstr, $"Expecting Int64 value for DateTime property {name}"); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, typeof(DeserialisationException).GetConstructor(new[] { typeof(string), typeof(Type) })); ilg.Emit(OpCodes.Throw); } ilg.MarkLabel(lbl); ilg.Emit(OpCodes.Ldloca, value); ilg.Emit(OpCodes.Ldloc, ticks); ilg.Emit(OpCodes.Call, typeof(DateTime).GetConstructor(new[] { typeof(long) })); }
public void Serialise(ILGenerator ilg, LocalBuilder value, LocalBuilder packer, 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, typeof(UnsafePacker).GetMethod(nameof(UnsafePacker.PackArrayHeader))); // begin loop var loopStart = ilg.DefineLabel(); var loopTest = ilg.DefineLabel(); var loopEnd = 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); ITypeProvider provider; if (!context.TryGetTypeProvider(elementValue.LocalType, out provider)) { throw new Exception($"Cannot serialise IReadOnlyList<> element type {value.LocalType}."); } provider.Serialise(ilg, elementValue, packer, context); // 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); // after loop ilg.MarkLabel(loopEnd); }
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 void Deserialise(ILGenerator ilg, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, LocalBuilder contextLocal, DasherContext context, UnexpectedFieldBehaviour unexpectedFieldBehaviour) { var nullableType = value.LocalType; var valueType = nullableType.GetGenericArguments().Single(); ITypeProvider valueProvider; if (!context.TryGetTypeProvider(valueType, out valueProvider)) { throw new Exception($"Unable to deserialise values of type Nullable<{valueType}> from MsgPack data."); } var lblNull = ilg.DefineLabel(); var lblExit = ilg.DefineLabel(); ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Call, typeof(Unpacker).GetMethod(nameof(Unpacker.TryReadNull))); ilg.Emit(OpCodes.Brtrue, lblNull); // non-null var nonNullValue = ilg.DeclareLocal(valueType); valueProvider.Deserialise(ilg, name, targetType, nonNullValue, unpacker, contextLocal, context, unexpectedFieldBehaviour); ilg.Emit(OpCodes.Ldloca, value); ilg.Emit(OpCodes.Ldloc, nonNullValue); ilg.Emit(OpCodes.Call, nullableType.GetConstructor(new [] { valueType })); ilg.Emit(OpCodes.Br, lblExit); ilg.MarkLabel(lblNull); // null ilg.Emit(OpCodes.Ldloca, value); ilg.Emit(OpCodes.Initobj, nullableType); ilg.MarkLabel(lblExit); }
public bool TryEmitDeserialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, LocalBuilder contextLocal, DasherContext context, UnexpectedFieldBehaviour unexpectedFieldBehaviour) { var tupleType = value.LocalType; var tupleSize = tupleType.GenericTypeArguments.Length; Debug.Assert(tupleSize > 1); // read array length var count = ilg.DeclareLocal(typeof(int)); ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Ldloca, count); ilg.Emit(OpCodes.Call, Methods.Unpacker_TryReadArrayLength); // verify read correctly throwBlocks.ThrowIfFalse(() => { ilg.Emit(OpCodes.Ldstr, "Expecting tuple data to be encoded as array"); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); // Ensure the array has the expected number of items ilg.Emit(OpCodes.Ldloc, count); ilg.Emit(OpCodes.Ldc_I4, tupleSize); throwBlocks.ThrowIfNotEqual(() => { ilg.Emit(OpCodes.Ldstr, $"Received array must have length {tupleSize} for type {tupleType.FullName}"); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); var success = true; var locals = new List <LocalBuilder>(); var i = 1; foreach (var type in tupleType.GenericTypeArguments) { var local = ilg.DeclareLocal(type); locals.Add(local); if (!DeserialiserEmitter.TryEmitDeserialiseCode(ilg, throwBlocks, errors, $"Item{i}", targetType, local, unpacker, context, contextLocal, unexpectedFieldBehaviour)) { errors.Add($"Unable to create deserialiser for tuple item type {type}"); success = false; } i++; } ilg.Emit(OpCodes.Ldloca, value); foreach (var local in locals) { ilg.Emit(OpCodes.Ldloc, local); } ilg.Emit(OpCodes.Call, tupleType.GetConstructor(tupleType.GenericTypeArguments)); return(success); }
public bool TryEmitDeserialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, LocalBuilder contextLocal, DasherContext context, UnexpectedFieldBehaviour unexpectedFieldBehaviour) { // Read value as a string var s = ilg.DeclareLocal(typeof(string)); ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Ldloca, s); ilg.Emit(OpCodes.Call, Methods.Unpacker_TryReadString); throwBlocks.ThrowIfFalse(() => { ilg.Emit(OpCodes.Ldstr, "Unable to read string value for enum property \"{0}\" of type \"{1}\""); ilg.Emit(OpCodes.Ldstr, name); ilg.LoadType(value.LocalType); ilg.Emit(OpCodes.Call, Methods.String_Format_String_Object_Object); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); ilg.Emit(OpCodes.Ldloc, s); ilg.Emit(OpCodes.Ldc_I4_1); ilg.Emit(OpCodes.Ldloca, value); ilg.Emit(OpCodes.Call, Methods.Enum_TryParse_OpenGeneric.MakeGenericMethod(value.LocalType)); throwBlocks.ThrowIfFalse(() => { ilg.Emit(OpCodes.Ldstr, "Unable to parse value \"{0}\" as a member of enum type \"{1}\""); ilg.Emit(OpCodes.Ldloc, s); ilg.LoadType(value.LocalType); ilg.Emit(OpCodes.Call, Methods.String_Format_String_Object_Object); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); return(true); }
public bool TryEmitSerialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, LocalBuilder value, LocalBuilder packer, LocalBuilder contextLocal, DasherContext context) { // write the string form of the value ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Ldloca, value); ilg.Emit(OpCodes.Constrained, value.LocalType); ilg.Emit(OpCodes.Callvirt, Methods.Object_ToString); ilg.Emit(OpCodes.Call, Methods.Packer_Pack_String); return(true); }
public bool TryEmitDeserialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, LocalBuilder contextLocal, DasherContext context, UnexpectedFieldBehaviour unexpectedFieldBehaviour) { ilg.Emit(OpCodes.Ldloc, unpacker); ilg.LoadType(targetType); ilg.Emit(OpCodes.Ldstr, name); ilg.Emit(OpCodes.Call, Methods.DecimalProvider_Parse); ilg.Emit(OpCodes.Stloc, value); return(true); }
public bool TryEmitDeserialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, LocalBuilder contextLocal, DasherContext context, UnexpectedFieldBehaviour unexpectedFieldBehaviour) { // read value as string var s = ilg.DeclareLocal(typeof(string)); ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Ldloca, s); ilg.Emit(OpCodes.Call, Methods.Unpacker_TryReadString); // If the unpacker method failed (returned false), throw throwBlocks.ThrowIfFalse(() => { ilg.Emit(OpCodes.Ldstr, $"Expecting string value for Version property {name}"); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); ilg.Emit(OpCodes.Ldloc, s); ilg.Emit(OpCodes.Newobj, typeof(Version).GetConstructor(new[] { typeof(string) })); ilg.Emit(OpCodes.Stloc, value); return(true); }
public void ConstructionPerf() { #if DEBUG Assert.True(false, "Performance comparison must be performed on a release build."); #endif const int iterations = 100; // Warm up the deserialiser, with a different type new Deserialiser <RecurringTree>(); //////////////////////////////////////////////////////////////////////////// Stopwatch buildTime; { TestUtils.CleanUpForPerfTest(); buildTime = Stopwatch.StartNew(); for (var i = 0; i < iterations; i++) { new Deserialiser <ClassWithAllDefaults>(); } buildTime.Stop(); } //////////////////////////////////////////////////////////////////////////// Stopwatch buildWithSharedContextTime; { TestUtils.CleanUpForPerfTest(); buildWithSharedContextTime = Stopwatch.StartNew(); var context = new DasherContext(); for (var i = 0; i < iterations; i++) { new Deserialiser <ClassWithAllDefaults>(context: context); } buildWithSharedContextTime.Stop(); } //////////////////////////////////////////////////////////////////////////// Stopwatch buildAndUseTime; { var stream = new MemoryStream(); new Packer(stream).PackMapHeader(0); var bytes = stream.ToArray(); var builtDeserialisers = Enumerable.Range(0, iterations).Select(i => new Deserialiser <ClassWithAllDefaults>()).ToList(); builtDeserialisers[0].Deserialise(bytes); TestUtils.CleanUpForPerfTest(); buildAndUseTime = Stopwatch.StartNew(); for (var i = 0; i < iterations; i++) { builtDeserialisers[i].Deserialise(bytes); } buildAndUseTime.Stop(); } //////////////////////////////////////////////////////////////////////////// Stopwatch buildAndUseWithSharedContextTime; { var stream = new MemoryStream(); new Packer(stream).PackMapHeader(0); var bytes = stream.ToArray(); var builtDeserialisers = Enumerable.Range(0, iterations).Select(i => new Deserialiser <ClassWithAllDefaults>()).ToList(); builtDeserialisers[0].Deserialise(bytes); TestUtils.CleanUpForPerfTest(); buildAndUseWithSharedContextTime = Stopwatch.StartNew(); for (var i = 0; i < iterations; i++) { builtDeserialisers[i].Deserialise(bytes); } buildAndUseWithSharedContextTime.Stop(); } //////////////////////////////////////////////////////////////////////////// _output.WriteLine($"{iterations} constructions in {buildTime.Elapsed.TotalMilliseconds} ms"); _output.WriteLine($"{iterations} constructions with shared context in {buildWithSharedContextTime.Elapsed.TotalMilliseconds} ms"); _output.WriteLine($"{iterations} deserialisations in {buildAndUseTime.Elapsed.TotalMilliseconds} ms"); _output.WriteLine($"{iterations} deserialisations with shared context in {buildAndUseWithSharedContextTime.Elapsed.TotalMilliseconds} ms"); }
public void Deserialise(ILGenerator ilg, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, LocalBuilder contextLocal, DasherContext context, UnexpectedFieldBehaviour unexpectedFieldBehaviour) { ilg.LoadType(value.LocalType); ilg.Emit(OpCodes.Ldc_I4, (int)unexpectedFieldBehaviour); ilg.Emit(OpCodes.Ldloc, contextLocal); ilg.Emit(OpCodes.Newobj, typeof(Deserialiser).GetConstructor(new[] { typeof(Type), typeof(UnexpectedFieldBehaviour), typeof(DasherContext) })); ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Call, typeof(Deserialiser).GetMethod(nameof(Deserialiser.Deserialise), new[] { typeof(Unpacker) })); ilg.Emit(OpCodes.Castclass, value.LocalType); ilg.Emit(OpCodes.Stloc, value); }
public void Serialise(ILGenerator ilg, LocalBuilder value, LocalBuilder packer, DasherContext context) { var type = value.LocalType; // treat as complex object and recur var props = type .GetProperties(BindingFlags.Instance | BindingFlags.Public) .Where(p => p.CanRead) .ToList(); // write map header ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Ldc_I4, props.Count); ilg.Emit(OpCodes.Call, typeof(UnsafePacker).GetMethod(nameof(UnsafePacker.PackMapHeader))); // 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, typeof(UnsafePacker).GetMethod(nameof(UnsafePacker.Pack), new[] { typeof(string) })); // get property value ilg.Emit(type.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc, value); ilg.Emit(OpCodes.Call, prop.GetMethod); ilg.Emit(OpCodes.Stloc, propValue); // find the property type's provider ITypeProvider provider; if (!context.TryGetTypeProvider(prop.PropertyType, out provider)) { throw new Exception($"Unable to serialise type {prop.PropertyType}"); } // write property value provider.Serialise(ilg, propValue, packer, context); } }
public bool TryEmitDeserialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, LocalBuilder contextLocal, DasherContext context, UnexpectedFieldBehaviour unexpectedFieldBehaviour) { // Read value as a long var ticks = ilg.DeclareLocal(typeof(long)); ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Ldloca, ticks); ilg.Emit(OpCodes.Call, Methods.Unpacker_TryReadInt64); // If the unpacker method failed (returned false), throw throwBlocks.ThrowIfFalse(() => { ilg.Emit(OpCodes.Ldstr, $"Expecting Int64 value for TimeSpan property {name}"); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); ilg.Emit(OpCodes.Ldloca, value); ilg.Emit(OpCodes.Ldloc, ticks); ilg.Emit(OpCodes.Call, Methods.TimeSpan_Ctor_Int64); return(true); }
public bool TryEmitSerialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, LocalBuilder value, LocalBuilder packer, LocalBuilder contextLocal, DasherContext context) { var tupleType = value.LocalType; var tupleSize = tupleType.GenericTypeArguments.Length; Debug.Assert(tupleSize > 1); // write the array header ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Ldc_I4, tupleSize); ilg.Emit(OpCodes.Call, Methods.Packer_PackArrayHeader); var success = true; // write each item's value var i = 1; foreach (var itemType in tupleType.GenericTypeArguments) { ilg.Emit(OpCodes.Ldloc, value); ilg.Emit(OpCodes.Ldfld, tupleType.GetField($"Item{i}")); var local = ilg.DeclareLocal(itemType); ilg.Emit(OpCodes.Stloc, local); if (!SerialiserEmitter.TryEmitSerialiseCode(ilg, throwBlocks, errors, local, packer, context, contextLocal)) { errors.Add($"Unable to serialise tuple item of type {itemType}"); success = false; } i++; } return(success); }
public bool TryEmitDeserialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, LocalBuilder contextLocal, DasherContext context, UnexpectedFieldBehaviour unexpectedFieldBehaviour) { var lblValidateArrayLength = ilg.DefineLabel(); var lblExit = ilg.DefineLabel(); // read the array length var count = ilg.DeclareLocal(typeof(int)); ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Ldloca, count); ilg.Emit(OpCodes.Call, Methods.Unpacker_TryReadArrayLength); ilg.Emit(OpCodes.Brtrue_S, lblValidateArrayLength); // test for empty map (empty type) ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Call, Methods.Unpacker_TryPeekEmptyMap); throwBlocks.ThrowIfFalse(() => { ilg.Emit(OpCodes.Ldstr, "Union values must be encoded as an array for property \"{0}\" of type \"{1}\""); ilg.Emit(OpCodes.Ldstr, name); ilg.LoadType(value.LocalType); ilg.Emit(OpCodes.Call, Methods.String_Format_String_Object_Object); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); ilg.Emit(OpCodes.Ldnull); ilg.Emit(OpCodes.Stloc, value); ilg.Emit(OpCodes.Br, lblExit); // ensure we have two items in the array ilg.MarkLabel(lblValidateArrayLength); ilg.Emit(OpCodes.Ldloc, count); ilg.Emit(OpCodes.Ldc_I4_2); throwBlocks.ThrowIfNotEqual(() => { ilg.Emit(OpCodes.Ldstr, "Union array should have 2 elements (not {0}) for property \"{1}\" of type \"{2}\""); ilg.Emit(OpCodes.Ldloc, count); ilg.Emit(OpCodes.Box, typeof(int)); ilg.Emit(OpCodes.Ldstr, name); ilg.LoadType(value.LocalType); ilg.Emit(OpCodes.Call, Methods.String_Format_String_Object_Object_Object); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); // read the serialised type name var typeName = ilg.DeclareLocal(typeof(string)); ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Ldloca, typeName); ilg.Emit(OpCodes.Call, Methods.Unpacker_TryReadString); throwBlocks.ThrowIfFalse(() => { ilg.Emit(OpCodes.Ldstr, "Unable to read union type name for property \"{0}\" of type \"{1}\""); ilg.Emit(OpCodes.Ldstr, name); ilg.LoadType(value.LocalType); ilg.Emit(OpCodes.Call, Methods.String_Format_String_Object_Object); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); var success = true; // loop through types within the union, looking for a matching type name var labelNextType = ilg.DefineLabel(); foreach (var type in value.LocalType.GetGenericArguments()) { var expectedTypeName = UnionEncoding.GetTypeName(type); ilg.Emit(OpCodes.Ldloc, typeName); ilg.Emit(OpCodes.Ldstr, expectedTypeName); ilg.Emit(OpCodes.Call, Methods.String_Equals_String_String); // continue if this type doesn't match the union's values ilg.Emit(OpCodes.Brfalse, labelNextType); // we have a match // read the value var readValue = ilg.DeclareLocal(type); if (!DeserialiserEmitter.TryEmitDeserialiseCode(ilg, throwBlocks, errors, name, targetType, readValue, unpacker, context, contextLocal, unexpectedFieldBehaviour)) { errors.Add($"Unable to deserialise union member type {type}"); success = false; } // create the union ilg.Emit(OpCodes.Ldloc, readValue); ilg.Emit(OpCodes.Call, value.LocalType.GetMethod(nameof(Union <int, int> .Create), new[] { type })); // store it in the result value ilg.Emit(OpCodes.Stloc, value); // exit the loop ilg.Emit(OpCodes.Br, lblExit); ilg.MarkLabel(labelNextType); labelNextType = ilg.DefineLabel(); } // If we exhaust the loop, throws ilg.MarkLabel(labelNextType); throwBlocks.Throw(() => { // TODO include received type name in error message and some more general info ilg.Emit(OpCodes.Ldstr, "No match on union type"); ilg.Emit(OpCodes.Newobj, Methods.Exception_Ctor_String); ilg.Emit(OpCodes.Throw); }); ilg.MarkLabel(lblExit); return(success); }
public void Serialise(ILGenerator ilg, LocalBuilder value, LocalBuilder packer, 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 lblNull = ilg.DefineLabel(); var lblExit = ilg.DefineLabel(); ilg.Emit(OpCodes.Brfalse, lblNull); // has a value to serialise ITypeProvider provider; if (!context.TryGetTypeProvider(valueType, out provider)) { throw new Exception($"Cannot serialise underlying type of Nullable<{valueType}>"); } 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); provider.Serialise(ilg, nonNullValue, packer, context); ilg.Emit(OpCodes.Br, lblExit); ilg.MarkLabel(lblNull); // value is null ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Call, typeof(UnsafePacker).GetMethod(nameof(UnsafePacker.PackNull))); ilg.MarkLabel(lblExit); }
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); }
public bool TryEmitDeserialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, LocalBuilder contextLocal, DasherContext context, UnexpectedFieldBehaviour unexpectedFieldBehaviour) { var rodicType = value.LocalType; var dicType = typeof(Dictionary <,>).MakeGenericType(rodicType.GenericTypeArguments); var keyType = rodicType.GetGenericArguments()[0]; var valueType = rodicType.GetGenericArguments()[1]; // read map length var count = ilg.DeclareLocal(typeof(int)); ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Ldloca, count); ilg.Emit(OpCodes.Call, Methods.Unpacker_TryReadMapLength); // verify read correctly throwBlocks.ThrowIfFalse(() => { ilg.Emit(OpCodes.Ldstr, "Expecting collection data to be encoded a map"); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); var keyValue = ilg.DeclareLocal(keyType); var valueValue = ilg.DeclareLocal(valueType); // create a mutable dictionary to store key/values var dic = ilg.DeclareLocal(dicType); ilg.Emit(OpCodes.Newobj, dicType.GetConstructor(Type.EmptyTypes)); ilg.Emit(OpCodes.Stloc, dic); // begin loop var loopStart = ilg.DefineLabel(); var loopTest = ilg.DefineLabel(); var loopEnd = 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 // read key if (!DeserialiserEmitter.TryEmitDeserialiseCode(ilg, throwBlocks, errors, name, targetType, keyValue, unpacker, context, contextLocal, unexpectedFieldBehaviour)) { errors.Add($"Unable to deserialise IReadOnlyDictionary<> key type {keyType} from MsgPack data."); return(false); } // read value if (!DeserialiserEmitter.TryEmitDeserialiseCode(ilg, throwBlocks, errors, name, targetType, valueValue, unpacker, context, contextLocal, unexpectedFieldBehaviour)) { errors.Add($"Unable to deserialise IReadOnlyDictionary<> value type {keyType} from MsgPack data."); return(false); } ilg.Emit(OpCodes.Ldloc, dic); ilg.Emit(OpCodes.Ldloc, keyValue); ilg.Emit(OpCodes.Ldloc, valueValue); ilg.Emit(OpCodes.Callvirt, dicType.GetMethod(nameof(Dictionary <int, int> .Add))); // 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); // after loop ilg.MarkLabel(loopEnd); ilg.Emit(OpCodes.Ldloc, dic); ilg.Emit(OpCodes.Stloc, value); return(true); }
public bool TryEmitDeserialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, LocalBuilder contextLocal, DasherContext context, UnexpectedFieldBehaviour unexpectedFieldBehaviour) { if (unexpectedFieldBehaviour == UnexpectedFieldBehaviour.Ignore) { // When ignoring unexpected fields, it doesn't matter what we receive in the message // as we will always accept the value and store 'null' on the target. ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Call, Methods.Unpacker_SkipValue); } else { var end = ilg.DefineLabel(); // Check for null ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Call, Methods.Unpacker_TryReadNull); ilg.Emit(OpCodes.Brtrue, end); var mapSize = ilg.DeclareLocal(typeof(int)); // Try to read the map header ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Ldloca, mapSize); ilg.Emit(OpCodes.Call, Methods.Unpacker_TryReadMapLength); // If the unpacker method failed (returned false), throw throwBlocks.ThrowIfFalse(() => { ilg.Emit(OpCodes.Ldstr, $"Unable to deserialise {nameof(Empty)} type for \"{{0}}\". Expected MsgPack format Null or Map, got {{1}}."); ilg.Emit(OpCodes.Ldstr, name); ilg.PeekFormatString(unpacker); ilg.Emit(OpCodes.Call, Methods.String_Format_String_Object_Object); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); ilg.Emit(OpCodes.Ldloc, mapSize); throwBlocks.ThrowIfTrue(() => { ilg.Emit(OpCodes.Ldstr, $"Unable to deserialise {nameof(Empty)} type for \"{{0}}\". Expected map with 0 entries, got {{1}}."); ilg.Emit(OpCodes.Ldstr, name); ilg.Emit(OpCodes.Ldloc, mapSize); ilg.Emit(OpCodes.Box, typeof(int)); ilg.Emit(OpCodes.Call, Methods.String_Format_String_Object_Object); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); ilg.MarkLabel(end); } ilg.Emit(OpCodes.Ldnull); ilg.Emit(OpCodes.Stloc, value); return(true); }
public void Serialise(ILGenerator ilg, LocalBuilder value, LocalBuilder packer, DasherContext context) { // write the int64 form of the value ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Ldloca, value); ilg.Emit(OpCodes.Call, typeof(IntPtr).GetMethod(nameof(IntPtr.ToInt64))); ilg.Emit(OpCodes.Call, typeof(UnsafePacker).GetMethod(nameof(UnsafePacker.Pack), new[] { typeof(long) })); }
public void Serialise(ILGenerator ilg, LocalBuilder value, LocalBuilder packer, DasherContext context) { // write the string form ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Ldloc, value); ilg.Emit(OpCodes.Call, typeof(Version).GetMethod(nameof(Version.ToString), new Type[0])); ilg.Emit(OpCodes.Call, typeof(UnsafePacker).GetMethod(nameof(UnsafePacker.Pack), new[] { typeof(string) })); }
public void Deserialise(ILGenerator ilg, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, LocalBuilder contextLocal, DasherContext context, UnexpectedFieldBehaviour unexpectedFieldBehaviour) { var elementType = value.LocalType.GetGenericArguments().Single(); ITypeProvider elementProvider; if (!context.TryGetTypeProvider(elementType, out elementProvider)) { throw new Exception($"Unable to deserialise values of type {elementType} from MsgPack data."); } // read list length var count = ilg.DeclareLocal(typeof(int)); ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Ldloca, count); ilg.Emit(OpCodes.Call, typeof(Unpacker).GetMethod(nameof(Unpacker.TryReadArrayLength))); // verify read correctly var lbl1 = ilg.DefineLabel(); ilg.Emit(OpCodes.Brtrue, lbl1); { ilg.Emit(OpCodes.Ldstr, "Expecting collection data to be encoded as array"); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, typeof(DeserialisationException).GetConstructor(new[] { typeof(string), typeof(Type) })); ilg.Emit(OpCodes.Throw); } ilg.MarkLabel(lbl1); // create an array to store values ilg.Emit(OpCodes.Ldloc, count); ilg.Emit(OpCodes.Newarr, elementType); var array = ilg.DeclareLocal(elementType.MakeArrayType()); ilg.Emit(OpCodes.Stloc, array); // begin loop var loopStart = ilg.DefineLabel(); var loopTest = ilg.DefineLabel(); var loopEnd = 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 var element = ilg.DeclareLocal(elementType); elementProvider.Deserialise(ilg, name, targetType, element, unpacker, contextLocal, context, unexpectedFieldBehaviour); ilg.Emit(OpCodes.Ldloc, array); ilg.Emit(OpCodes.Ldloc, i); ilg.Emit(OpCodes.Ldloc, element); ilg.Emit(OpCodes.Stelem, elementType); // 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); // after loop ilg.MarkLabel(loopEnd); ilg.Emit(OpCodes.Ldloc, array); ilg.Emit(OpCodes.Stloc, value); }
public bool TryEmitSerialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, LocalBuilder value, LocalBuilder packer, LocalBuilder contextLocal, DasherContext context) { // We need to write both the date and the offset // - dto.DateTime always has unspecified kind (so we can just use Ticks rather than ToBinary and ignore internal flags) // - dto.Offset is a timespan but always has integral minutes (minutes will be a smaller number than ticks so uses fewer bytes on the wire) ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Dup); ilg.Emit(OpCodes.Dup); // Write the array header ilg.Emit(OpCodes.Ldc_I4_2); ilg.Emit(OpCodes.Call, Methods.Packer_PackArrayHeader); // Write ticks ilg.Emit(OpCodes.Ldloca, value); ilg.Emit(OpCodes.Call, Methods.DateTimeOffset_Ticks_Get); ilg.Emit(OpCodes.Call, Methods.Packer_Pack_Int64); // Write offset minutes var offset = ilg.DeclareLocal(typeof(TimeSpan)); ilg.Emit(OpCodes.Ldloca, value); ilg.Emit(OpCodes.Call, Methods.DateTimeOffset_Offset_Get); ilg.Emit(OpCodes.Stloc, offset); ilg.Emit(OpCodes.Ldloca, offset); ilg.Emit(OpCodes.Call, Methods.TimeSpan_Ticks_Get); ilg.Emit(OpCodes.Ldc_I4, TicksPerMinute); ilg.Emit(OpCodes.Conv_I8); ilg.Emit(OpCodes.Div); ilg.Emit(OpCodes.Conv_I2); ilg.Emit(OpCodes.Call, Methods.Packer_Pack_Int16); return(true); }
public void Serialise(ILGenerator ilg, LocalBuilder value, LocalBuilder packer, DasherContext context) { // write the ticks form of the value as int64 ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Ldloca, value); ilg.Emit(OpCodes.Call, typeof(DateTime).GetProperty(nameof(DateTime.Ticks)).GetMethod); ilg.Emit(OpCodes.Call, typeof(UnsafePacker).GetMethod(nameof(UnsafePacker.Pack), new[] { typeof(long) })); }
public bool TryEmitDeserialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, LocalBuilder contextLocal, DasherContext context, UnexpectedFieldBehaviour unexpectedFieldBehaviour) { // Ensure we have an array of two values var arrayLength = ilg.DeclareLocal(typeof(int)); ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Ldloca, arrayLength); ilg.Emit(OpCodes.Call, Methods.Unpacker_TryReadArrayLength); // If the unpacker method failed (returned false), throw throwBlocks.ThrowIfFalse(() => { ilg.Emit(OpCodes.Ldstr, $"Expecting array header for DateTimeOffset property {name}"); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); ilg.Emit(OpCodes.Ldloc, arrayLength); ilg.Emit(OpCodes.Ldc_I4_2); throwBlocks.ThrowIfNotEqual(() => { ilg.Emit(OpCodes.Ldstr, $"Expecting array to contain two items for DateTimeOffset property {name}"); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); // Read ticks var ticks = ilg.DeclareLocal(typeof(long)); ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Ldloca, ticks); ilg.Emit(OpCodes.Call, Methods.Unpacker_TryReadInt64); // If the unpacker method failed (returned false), throw throwBlocks.ThrowIfFalse(() => { ilg.Emit(OpCodes.Ldstr, $"Expecting Int64 value for ticks component of DateTimeOffset property {name}"); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); // Read offset var minutes = ilg.DeclareLocal(typeof(short)); ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Ldloca, minutes); ilg.Emit(OpCodes.Call, Methods.Unpacker_TryReadInt16); // If the unpacker method failed (returned false), throw throwBlocks.ThrowIfFalse(() => { ilg.Emit(OpCodes.Ldstr, $"Expecting Int16 value for offset component of DateTimeOffset property {name}"); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); // Compose the final DateTimeOffset ilg.Emit(OpCodes.Ldloca, value); ilg.Emit(OpCodes.Ldloc, ticks); ilg.Emit(OpCodes.Ldloc, minutes); ilg.Emit(OpCodes.Conv_I8); ilg.Emit(OpCodes.Ldc_I4, TicksPerMinute); ilg.Emit(OpCodes.Conv_I8); ilg.Emit(OpCodes.Mul); ilg.Emit(OpCodes.Conv_I8); ilg.Emit(OpCodes.Call, Methods.TimeSpan_FromTicks); ilg.Emit(OpCodes.Call, Methods.DateTimeOffset_Ctor_Long_TimeSpan); return(true); }
public bool TryEmitDeserialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, LocalBuilder contextLocal, DasherContext context, UnexpectedFieldBehaviour unexpectedFieldBehaviour) { ilg.Emit(OpCodes.Ldloc, unpacker); ilg.Emit(OpCodes.Ldloca, value); ilg.Emit(OpCodes.Call, Methods.Unpacker_TryReadByteArraySegment); throwBlocks.ThrowIfFalse(() => { // not binary and not null ilg.Emit(OpCodes.Ldstr, "Unexpected MsgPack format for \"{0}\". Expected {1}, got {2}."); ilg.Emit(OpCodes.Ldstr, name); ilg.Emit(OpCodes.Ldstr, value.LocalType.Name); ilg.PeekFormatString(unpacker); ilg.Emit(OpCodes.Call, Methods.String_Format_String_Object_Object_Object); ilg.LoadType(targetType); ilg.Emit(OpCodes.Newobj, Methods.DeserialisationException_Ctor_String_Type); ilg.Emit(OpCodes.Throw); }); return(true); }
public bool TryEmitSerialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, LocalBuilder value, LocalBuilder packer, LocalBuilder contextLocal, DasherContext context) { // write the ticks form of the value as int64 ilg.Emit(OpCodes.Ldloc, packer); ilg.Emit(OpCodes.Ldloca, value); ilg.Emit(OpCodes.Call, Methods.TimeSpan_Ticks_Get); ilg.Emit(OpCodes.Call, Methods.Packer_Pack_Int64); return(true); }