Esempio n. 1
0
        public static Action <Packer, DasherContext, object> Build(Type type, DasherContext context)
        {
            // Create a collection for errors, initially with zero capacity (minimal allocation)
            var errors = new List <string>(0);

            // Validate that the type is suitable as a top-level type
            context.ValidateTopLevelType(type, errors);

            // Throw if there were any errors
            if (errors.Any())
            {
                throw new SerialiserGenerationException(errors, type);
            }

            // Initialise code gen
            var method = new DynamicMethod(
                $"Generated{type.Name}Serialiser",
                returnType: null,
                parameterTypes: new[] { typeof(Packer), typeof(DasherContext), typeof(object) },
                restrictedSkipVisibility: true);

            var ilg = method.GetILGenerator();

            // Convert args to locals, so we can pass them around
            var packer = ilg.DeclareLocal(typeof(Packer));

            ilg.Emit(OpCodes.Ldarg_0); // packer
            ilg.Emit(OpCodes.Stloc, packer);

            var contextLocal = ilg.DeclareLocal(typeof(DasherContext));

            ilg.Emit(OpCodes.Ldarg_1); // context
            ilg.Emit(OpCodes.Stloc, contextLocal);

            var value = ilg.DeclareLocal(type);

            ilg.Emit(OpCodes.Ldarg_2); // value
            ilg.Emit(type.GetTypeInfo().IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, type);
            ilg.Emit(OpCodes.Stloc, value);

            var throwBlocks = new ThrowBlockGatherer(ilg);

            if (!TryEmitSerialiseCode(ilg, throwBlocks, errors, value, packer, context, contextLocal, isRoot: true))
            {
                Debug.Assert(errors.Any());
                throw new SerialiserGenerationException(errors, type);
            }

            // Return the newly constructed object!
            ilg.Emit(OpCodes.Ret);

            // Write all the exception handling blocks out of line
            throwBlocks.Flush();

            // Return a delegate that performs the above operations
            return((Action <Packer, DasherContext, object>)method.CreateDelegate(typeof(Action <Packer, DasherContext, object>)));
        }
Esempio n. 2
0
 /// <summary>
 /// Initialises an instance of the deserialiser.
 /// </summary>
 /// <remarks>
 /// This constructor attempts to retrieve generated deserialisation code from <paramref name="context"/>.
 /// If unavailable, it will generate the relevant deserialisation code and cache it in <paramref name="context"/> for future use.
 /// </remarks>
 /// <param name="unexpectedFieldBehaviour">Controls how deserialisation behaves when unexpected data is received. Default is <see cref="UnexpectedFieldBehaviour.Throw"/>.</param>
 /// <param name="context">An optional context for deserialisation. If none is provided, a new context will be created for use by this deserialiser alone.</param>
 public Deserialiser(UnexpectedFieldBehaviour unexpectedFieldBehaviour = UnexpectedFieldBehaviour.Throw, DasherContext context = null)
 {
     _context = context ?? new DasherContext();
     _func    = _context.GetOrCreateDeserialiseFunc(typeof(T), unexpectedFieldBehaviour);
 }
Esempio n. 3
0
 /// <summary>
 /// Initialises an instance of the serialiser.
 /// </summary>
 /// <remarks>
 /// This constructor attempts to retrieve generated serialisation code from <paramref name="context"/>.
 /// If unavailable, it will generate the relevant serialisation code and cache it in <paramref name="context"/> for future use.
 /// </remarks>
 /// <param name="context">An optional context for serialisation. If none is provided, a new context will be created for use by this serialiser alone.</param>
 public Serialiser(DasherContext context = null)
 {
     _context = context ?? new DasherContext();
     _action  = _context.GetOrCreateSerialiseAction(typeof(T));
 }
Esempio n. 4
0
        public static bool TryEmitDeserialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, string name, Type targetType, LocalBuilder value, LocalBuilder unpacker, DasherContext context, LocalBuilder contextLocal, UnexpectedFieldBehaviour unexpectedFieldBehaviour, bool isRoot = false)
        {
            if (!context.TryGetTypeProvider(value.LocalType, errors, out ITypeProvider provider))
            {
                return(false);
            }

            if (!isRoot && provider is ComplexTypeProvider)
            {
                ilg.Emit(OpCodes.Ldloc, contextLocal);
                ilg.LoadType(value.LocalType);
                ilg.Emit(OpCodes.Ldc_I4, (int)unexpectedFieldBehaviour);
                ilg.Emit(OpCodes.Call, Methods.DasherContext_GetOrCreateDeserialiseFunc);
                ilg.Emit(OpCodes.Ldloc, unpacker);
                ilg.Emit(OpCodes.Ldloc, contextLocal);
                ilg.Emit(OpCodes.Call, Methods.DasherDeserialiseFunc_Invoke);
                ilg.Emit(value.LocalType.GetTypeInfo().IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, value.LocalType);
                ilg.Emit(OpCodes.Stloc, value);
            }
            else
            {
                var end = ilg.DefineLabel();

                if (provider.UseDefaultNullHandling(value.LocalType))
                {
                    // check for null
                    var nonNullLabel = ilg.DefineLabel();
                    ilg.Emit(OpCodes.Ldloc, unpacker);
                    ilg.Emit(OpCodes.Call, Methods.Unpacker_TryReadNull);
                    ilg.Emit(OpCodes.Brfalse_S, nonNullLabel);
                    {
                        ilg.Emit(OpCodes.Ldnull);
                        ilg.Emit(OpCodes.Stloc, value);
                        ilg.Emit(OpCodes.Br, end);
                    }
                    ilg.MarkLabel(nonNullLabel);
                }

                if (!provider.TryEmitDeserialiseCode(ilg, throwBlocks, errors, name, targetType, value, unpacker, contextLocal, context, unexpectedFieldBehaviour))
                {
                    return(false);
                }

                ilg.MarkLabel(end);
            }
            return(true);
        }
Esempio n. 5
0
        public static bool TryEmitSerialiseCode(ILGenerator ilg, ThrowBlockGatherer throwBlocks, ICollection <string> errors, LocalBuilder value, LocalBuilder packer, DasherContext context, LocalBuilder contextLocal, bool isRoot = false)
        {
            if (!context.TryGetTypeProvider(value.LocalType, errors, out ITypeProvider provider))
            {
                return(false);
            }

            if (!isRoot && provider is ComplexTypeProvider)
            {
                // prevent endless code generation for recursive types by delegating to a method call
                ilg.Emit(OpCodes.Ldloc, contextLocal);
                ilg.LoadType(value.LocalType);
                ilg.Emit(OpCodes.Call, Methods.DasherContext_GetOrCreateSerialiseAction);

                ilg.Emit(OpCodes.Ldloc, packer);
                ilg.Emit(OpCodes.Ldloc, contextLocal);
                ilg.Emit(OpCodes.Ldloc, value);
                if (value.LocalType.GetTypeInfo().IsValueType)
                {
                    ilg.Emit(OpCodes.Box, value.LocalType);
                }
                ilg.Emit(OpCodes.Call, Methods.DasherSerialiseAction_Invoke);
            }
            else
            {
                var end = ilg.DefineLabel();

                if (provider.UseDefaultNullHandling(value.LocalType))
                {
                    var nonNull = ilg.DefineLabel();
                    ilg.Emit(OpCodes.Ldloc, value);
                    ilg.Emit(OpCodes.Brtrue_S, nonNull);
                    ilg.Emit(OpCodes.Ldloc, packer);
                    ilg.Emit(OpCodes.Call, Methods.Packer_PackNull);
                    ilg.Emit(OpCodes.Br, end);
                    ilg.MarkLabel(nonNull);
                }

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

                ilg.MarkLabel(end);
            }
            return(true);
        }