Beispiel #1
0
        public bool TryEmitSerialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, LocalBuilder value, LocalBuilder packer, LocalBuilder contextLocal, DasherContext context)
        {
            var type        = value.LocalType;
            var elementType = type.GetGenericArguments().Single();

            // read list length
            var count = ilg.DeclareLocal(typeof(int));

            ilg.Emit(OpCodes.Ldloc, value);
            ilg.Emit(OpCodes.Callvirt, typeof(IReadOnlyCollection <>).MakeGenericType(elementType).GetProperty(nameof(IReadOnlyList <int> .Count)).GetMethod);
            ilg.Emit(OpCodes.Stloc, count);

            // write array header
            ilg.Emit(OpCodes.Ldloc, packer);
            ilg.Emit(OpCodes.Ldloc, count);
            ilg.Emit(OpCodes.Call, Methods.Packer_PackArrayHeader);

            // begin loop
            var loopStart = ilg.DefineLabel();
            var loopTest  = ilg.DefineLabel();

            var i = ilg.DeclareLocal(typeof(int));

            ilg.Emit(OpCodes.Ldc_I4_0);
            ilg.Emit(OpCodes.Stloc, i);

            ilg.Emit(OpCodes.Br, loopTest);
            ilg.MarkLabel(loopStart);

            // loop body
            ilg.Emit(OpCodes.Ldloc, value);
            ilg.Emit(OpCodes.Ldloc, i);
            ilg.Emit(OpCodes.Callvirt, type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Single(p => p.Name == "Item" && p.GetIndexParameters().Length == 1).GetMethod);
            var elementValue = ilg.DeclareLocal(elementType);

            ilg.Emit(OpCodes.Stloc, elementValue);

            if (!SerialiserEmitter.TryEmitSerialiseCode(ilg, throwBlocks, errors, elementValue, packer, context, contextLocal))
            {
                errors.Add($"Cannot serialise IReadOnlyList<> element type {elementType}.");
                return(false);
            }

            // loop counter increment
            ilg.Emit(OpCodes.Ldloc, i);
            ilg.Emit(OpCodes.Ldc_I4_1);
            ilg.Emit(OpCodes.Add);
            ilg.Emit(OpCodes.Stloc, i);

            // loop test
            ilg.MarkLabel(loopTest);
            ilg.Emit(OpCodes.Ldloc, i);
            ilg.Emit(OpCodes.Ldloc, count);
            ilg.Emit(OpCodes.Clt);
            ilg.Emit(OpCodes.Brtrue, loopStart);

            return(true);
        }
        public bool TryEmitSerialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, LocalBuilder value, LocalBuilder packer, LocalBuilder contextLocal, DasherContext context)
        {
            // treat as complex object and recur
            var props = value.LocalType
                        .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                        .Where(p => p.CanRead)
                        .ToList();

            if (props.Count == 0)
            {
                throw new SerialisationException($"Unable to serialise type \"{value.LocalType}\". It has no dedicated type provider, and has no properties for serialisation as a complex type.", value.LocalType);
            }

            // write map header
            ilg.Emit(OpCodes.Ldloc, packer);
            ilg.Emit(OpCodes.Ldc_I4, props.Count);
            ilg.Emit(OpCodes.Call, Methods.Packer_PackMapHeader);

            var success = true;

            // write each property's value
            foreach (var prop in props)
            {
                var propValue = ilg.DeclareLocal(prop.PropertyType);

                // write property name
                ilg.Emit(OpCodes.Ldloc, packer);
                ilg.Emit(OpCodes.Ldstr, prop.Name);
                ilg.Emit(OpCodes.Call, Methods.Packer_Pack_String);

                // get property value
                ilg.Emit(value.LocalType.GetTypeInfo().IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc, value);
                ilg.Emit(OpCodes.Call, prop.GetMethod);
                ilg.Emit(OpCodes.Stloc, propValue);

                success &= SerialiserEmitter.TryEmitSerialiseCode(ilg, throwBlocks, errors, propValue, packer, context, contextLocal);
            }

            return(success);
        }
Beispiel #3
0
        public bool TryEmitSerialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, LocalBuilder value, LocalBuilder packer, LocalBuilder contextLocal, DasherContext context)
        {
            var type      = value.LocalType;
            var valueType = type.GetGenericArguments().Single();

            ilg.Emit(OpCodes.Ldloca, value);
            ilg.Emit(OpCodes.Call, type.GetProperty(nameof(Nullable <int> .HasValue)).GetMethod);

            var lblNonNull = ilg.DefineLabel();
            var lblExit    = ilg.DefineLabel();

            ilg.Emit(OpCodes.Brtrue_S, lblNonNull);

            // value is null
            ilg.Emit(OpCodes.Ldloc, packer);
            ilg.Emit(OpCodes.Call, Methods.Packer_PackNull);

            ilg.Emit(OpCodes.Br, lblExit);

            ilg.MarkLabel(lblNonNull);

            // has a value to serialise
            var nonNullValue = ilg.DeclareLocal(valueType);

            ilg.Emit(OpCodes.Ldloca, value);
            ilg.Emit(OpCodes.Call, type.GetProperty(nameof(Nullable <int> .Value)).GetMethod);
            ilg.Emit(OpCodes.Stloc, nonNullValue);

            if (!SerialiserEmitter.TryEmitSerialiseCode(ilg, throwBlocks, errors, nonNullValue, packer, context, contextLocal))
            {
                return(false);
            }

            ilg.MarkLabel(lblExit);

            return(true);
        }
Beispiel #4
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);
        }
Beispiel #5
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);
        }