public static Func <Unpacker, DasherContext, object> Build(Type type, UnexpectedFieldBehaviour unexpectedFieldBehaviour, 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 DeserialiserGenerationException(errors, type); } // Initialise code gen var method = new DynamicMethod( $"Generated{type.Name}Deserialiser", returnType: typeof(object), parameterTypes: new[] { typeof(Unpacker), typeof(DasherContext) }, restrictedSkipVisibility: true); var ilg = method.GetILGenerator(); // Convert args to locals, so we can pass them around var unpacker = ilg.DeclareLocal(typeof(Unpacker)); ilg.Emit(OpCodes.Ldarg_0); ilg.Emit(OpCodes.Stloc, unpacker); var contextLocal = ilg.DeclareLocal(typeof(DasherContext)); ilg.Emit(OpCodes.Ldarg_1); ilg.Emit(OpCodes.Stloc, contextLocal); var valueLocal = ilg.DeclareLocal(type); var throwBlocks = new ThrowBlockGatherer(ilg); if (!TryEmitDeserialiseCode(ilg, throwBlocks, errors, "<root>", type, valueLocal, unpacker, context, contextLocal, unexpectedFieldBehaviour, isRoot: true)) { Debug.Assert(errors.Any()); throw new DeserialiserGenerationException(errors, type); } ilg.Emit(OpCodes.Ldloc, valueLocal); if (type.GetTypeInfo().IsValueType) { ilg.Emit(OpCodes.Box, 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((Func <Unpacker, DasherContext, object>)method.CreateDelegate(typeof(Func <Unpacker, DasherContext, object>))); }
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); }
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); }