// emits code to clear all the fields (public, private, readonly) that meet the specified criteria internal static void EmitClearFields(Type type, KnownSerializers serializers, ILGenerator il) { // simply invoke the type serializer for each relevant field foreach (FieldInfo field in GetClonableFields(type)) { if (IsSimpleValueType(field.FieldType) || field.FieldType == typeof(string)) { continue; } // find the right handler var handler = serializers.GetUntypedHandler(field.FieldType); int index = serializers.GetIndex(handler); // argument legend: 0 = ref target, 1 = context il.Emit(OpCodes.Ldarg_1); // push context il.Emit(OpCodes.Ldc_I4, index); // push the serializer id il.Emit(OpCodes.Call, GetHandlerFromIndexMethod); // call GetHandler to push the handler // handler.Clear(ref target.Field, context); // il.EmitWriteLine(string.Format("Clear(ref target.{0});", field.Name)); il.Emit(OpCodes.Ldarg_0); // target if (type.IsClass) { il.Emit(OpCodes.Ldind_Ref); } il.Emit(OpCodes.Ldflda, field); // target.field & il.Emit(OpCodes.Ldarg_1); // context var mi = handler.GetType().GetMethod(nameof(SerializationHandler <int> .Clear), BindingFlags.Instance | BindingFlags.Public); il.Emit(OpCodes.Call, mi); } il.Emit(OpCodes.Ret); }
// ********************** // the following methods do the heavy lifting of generating the IL code for each operation we support // ********************** // emits code to clone all the fields (public, private, readonly) that meet the specified criteria internal static void EmitCloneFields(Type type, KnownSerializers serializers, ILGenerator il) { // simply invoke the type serializer for each relevant field foreach (FieldInfo field in GetClonableFields(type)) { // argument legend: 0 = source, 1 = ref target, 2 = context if (IsSimpleValueType(field.FieldType) || field.FieldType == typeof(string)) { // for primitive types, simply copy the value from one field to the other il.Emit(OpCodes.Ldarg_1); // target if (type.IsClass) { il.Emit(OpCodes.Ldind_Ref); } il.Emit(OpCodes.Ldarg_0); // source il.Emit(OpCodes.Ldfld, field); // source.field il.Emit(OpCodes.Stfld, field); // target.field } else { // find the right serializer var handler = serializers.GetUntypedHandler(field.FieldType); int index = serializers.GetIndex(handler); il.Emit(OpCodes.Ldarg_2); // push context il.Emit(OpCodes.Ldc_I4, index); // push the serializer id il.Emit(OpCodes.Call, GetHandlerFromIndexMethod); // call GetHandler to push the handler // simply invoke the type serializer for each relevant field il.Emit(OpCodes.Ldarg_0); // source il.Emit(OpCodes.Ldfld, field); // source.field il.Emit(OpCodes.Ldarg_1); // target if (type.IsClass) { il.Emit(OpCodes.Ldind_Ref); } il.Emit(OpCodes.Ldflda, field); // target.field & il.Emit(OpCodes.Ldarg_2); // context // handler.Clone(source.field, target.field, context); var mi = handler.GetType().GetMethod(nameof(SerializationHandler <int> .Clone), BindingFlags.Public | BindingFlags.Instance); il.Emit(OpCodes.Call, mi); // we have the right type, no need for callvirt } } il.Emit(OpCodes.Ret); }
// emits code to serialize all the fields (public, private, readonly) that meet the specified criteria internal static void EmitSerializeFields(Type type, KnownSerializers serializers, ILGenerator il, IEnumerable <MemberInfo> members = null) { // enumerate all fields and serialize each of them foreach (MemberInfo member in members ?? GetClonableFields(type)) { // inner loop generates this code for each field or property: // var handler = GetHandlerFromIndex(index); // handler.Serialize(writer, source.fieldOrProp, context); // make sure the member is valid ValidateMember(type, member); // find the right serializer var mt = (member.MemberType == MemberTypes.Field) ? ((FieldInfo)member).FieldType : ((PropertyInfo)member).PropertyType; var handler = serializers.GetUntypedHandler(mt); int index = serializers.GetIndex(handler); il.Emit(OpCodes.Ldarg_2); // push context il.Emit(OpCodes.Ldc_I4, index); // push the serializer id il.Emit(OpCodes.Call, GetHandlerFromIndexMethod); // call GetHandler to push the handler // push the arguments il.Emit(OpCodes.Ldarg_0); // writer il.Emit(OpCodes.Ldarg_1); // source if (member.MemberType == MemberTypes.Field) { il.Emit(OpCodes.Ldfld, (FieldInfo)member); // source.field } else { var getter = ((PropertyInfo)member).GetGetMethod(true); il.Emit(getter.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, getter); } il.Emit(OpCodes.Ldarg_2); // context // call handler.Serialize(writer, source.field, context) var mi = handler.GetType().GetMethod(nameof(SerializationHandler <int> .Serialize), BindingFlags.Instance | BindingFlags.Public); il.Emit(OpCodes.Call, mi); // we have the right type, no need for callvirt } il.Emit(OpCodes.Ret); }
// emits code to deserialize all the fields (public, private, readonly) that meet the specified criteria internal static void EmitDeserializeFields(Type type, KnownSerializers serializers, ILGenerator il, IEnumerable <MemberInfo> members = null) { LocalBuilder localBuilder = null; // enumerate all fields and deserialize each of them foreach (MemberInfo member in members ?? GetClonableFields(type)) { // inner loop generates this code for each field or property: // var handler = GetHandlerFromIndex(index); // handler.Deserialize(reader, ref target.fieldOrProp, context) // make sure the member is valid ValidateMember(type, member); // find the right serializer first var mt = (member.MemberType == MemberTypes.Field) ? ((FieldInfo)member).FieldType : ((PropertyInfo)member).PropertyType; var handler = serializers.GetUntypedHandler(mt); int index = serializers.GetIndex(handler); il.Emit(OpCodes.Ldarg_2); // push context il.Emit(OpCodes.Ldc_I4, index); // push the serializer id il.Emit(OpCodes.Call, GetHandlerFromIndexMethod); // call GetHandler to push the handler // argument legend: 0 = reader, 1 = ref target, 2 = context il.Emit(OpCodes.Ldarg_0); // reader il.Emit(OpCodes.Ldarg_1); // target& if (type.IsClass) { il.Emit(OpCodes.Ldind_Ref); } if (member.MemberType == MemberTypes.Field) { il.Emit(OpCodes.Ldflda, (FieldInfo)member); // target.field & } else { var getter = ((PropertyInfo)member).GetGetMethod(true); il.Emit(getter.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, getter); localBuilder = il.DeclareLocal(mt); il.Emit(OpCodes.Stloc, localBuilder); il.Emit(OpCodes.Ldloca, localBuilder); } il.Emit(OpCodes.Ldarg_2); // context // call handler.Deserialize(reader, ref target.field, context) var mi = handler.GetType().GetMethod(nameof(SerializationHandler <int> .Deserialize), BindingFlags.Instance | BindingFlags.Public); il.Emit(OpCodes.Call, mi); // not callvirt, since we have the right type // for properties, call the setter with the result if (member.MemberType == MemberTypes.Property) { il.Emit(OpCodes.Ldarg_1); // target& if (type.IsClass) { il.Emit(OpCodes.Ldind_Ref); } il.Emit(OpCodes.Ldloc, localBuilder); // deserialization result var setter = ((PropertyInfo)member).GetSetMethod(true); il.Emit(setter.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, setter); } } il.Emit(OpCodes.Ret); }
// ********************** // the following methods do the heavy lifting of generating the IL code for each operation we support // ********************** // emits code to clone all the fields (public, private, readonly) that meet the specified criteria internal static void EmitCloneFields(Type type, KnownSerializers serializers, ILGenerator il) { var cloningFlags = serializers.GetCloningFlags(type); // simply invoke the type serializer for each relevant field foreach (FieldInfo field in GetAllFields(type)) { // fields with NonSerialized attribute should be skipped if SkipNonSerializedFields is set if (field.IsNotSerialized && cloningFlags.HasFlag(CloningFlags.SkipNonSerializedFields)) { continue; } // emit exceptions for other non-clonable fields else if ((field.FieldType == typeof(IntPtr) || field.FieldType == typeof(UIntPtr)) && !cloningFlags.HasFlag(CloningFlags.CloneIntPtrFields)) { EmitException(typeof(NotSupportedException), $"Cannot clone field:{field.Name} because cloning of {field.FieldType.Name} fields is disabled by default. To enable cloning of {field.FieldType.Name} fields for the containing type, register the type {type.AssemblyQualifiedName} with the {CloningFlags.CloneIntPtrFields} flag.", il); } else if (field.FieldType.IsPointer && !cloningFlags.HasFlag(CloningFlags.ClonePointerFields)) { EmitException(typeof(NotSupportedException), $"Cannot clone field:{field.Name} because cloning of pointer fields is disabled by default. To enable cloning of pointer fields for the containing type, register the type {type.AssemblyQualifiedName} with the {CloningFlags.ClonePointerFields} flag.", il); } // emit cloning code for clonable fields // argument legend: 0 = source, 1 = ref target, 2 = context else if (IsSimpleValueType(field.FieldType) || field.FieldType == typeof(string) || field.FieldType.IsPointer) { // for primitive types, simply copy the value from one field to the other il.Emit(OpCodes.Ldarg_1); // target if (type.IsClass) { il.Emit(OpCodes.Ldind_Ref); } il.Emit(OpCodes.Ldarg_0); // source il.Emit(OpCodes.Ldfld, field); // source.field il.Emit(OpCodes.Stfld, field); // target.field } else { // find the right serializer var handler = serializers.GetUntypedHandler(field.FieldType); int index = serializers.GetIndex(handler); il.Emit(OpCodes.Ldarg_2); // push context il.Emit(OpCodes.Ldc_I4, index); // push the serializer id il.Emit(OpCodes.Call, GetHandlerFromIndexMethod); // call GetHandler to push the handler // simply invoke the type serializer for each relevant field il.Emit(OpCodes.Ldarg_0); // source il.Emit(OpCodes.Ldfld, field); // source.field il.Emit(OpCodes.Ldarg_1); // target if (type.IsClass) { il.Emit(OpCodes.Ldind_Ref); } il.Emit(OpCodes.Ldflda, field); // target.field & il.Emit(OpCodes.Ldarg_2); // context // handler.Clone(source.field, target.field, context); var mi = handler.GetType().GetMethod(nameof(SerializationHandler <int> .Clone), BindingFlags.Public | BindingFlags.Instance); il.Emit(OpCodes.Call, mi); // we have the right type, no need for callvirt } } il.Emit(OpCodes.Ret); }