示例#1
0
        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?)));
        }
示例#2
0
        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);
        }
示例#3
0
        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);
        }
示例#4
0
        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) }));
        }
示例#5
0
        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);
        }
示例#6
0
        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);
        }
示例#7
0
        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);
        }
示例#8
0
        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);
        }
示例#9
0
        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);
        }
示例#10
0
        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);
        }
示例#11
0
        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);
        }
示例#12
0
        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);
        }
示例#13
0
        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");
        }
示例#14
0
 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);
 }
示例#15
0
        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);
            }
        }
示例#16
0
        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);
        }
示例#17
0
        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);
        }
示例#18
0
        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);
        }
示例#19
0
        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);
        }
示例#20
0
        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);
        }
示例#21
0
        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);
        }
示例#22
0
        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);
        }
示例#23
0
 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) }));
 }
示例#24
0
 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) }));
 }
示例#25
0
        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);
        }
示例#26
0
        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);
        }
示例#27
0
 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) }));
 }
示例#28
0
        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);
        }
示例#29
0
        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);
        }
示例#30
0
        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);
        }