/// <summary> /// Determines whether an instance of the specified type must be cleared prior to reuse /// as a cloning or deserialization target. /// </summary> /// <param name="type">The type to check.</param> /// <param name="serializers">A registry of known serializers.</param> /// <returns>true if the type requires clearing prior to reuse; otherwise, false.</returns> internal static bool IsClearRequired(Type type, KnownSerializers serializers) { // first check whether a cached result exists var handler = serializers.GetUntypedHandler(type); if (handler.IsClearRequired.HasValue) { return(handler.IsClearRequired.Value); } // Initialize the cached value so that we know that we have seen it if we encounter it again during // the current evaluation (i.e. if the object graph contains a cycle back to the current type). handler.IsClearRequired = false; // Skip evaluation for simple value types and strings. Otherwise, clearing is only required // for Shared<> types, arrays of Shared<>, or object graphs which may contain a Shared<>. bool result = !IsSimpleValueType(type) && type != typeof(string) && ((type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Shared <>)) || (type.IsArray && IsClearRequired(type.GetElementType(), serializers)) || GetSerializableFields(type).Any(fi => IsClearRequired(fi.FieldType, serializers))); // cache the result handler.IsClearRequired = result; return(result); }
// 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); }
public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema) { var schema = TypeSchema.FromType(typeof(T), serializers.RuntimeVersion, this.GetType(), Version); this.serializeImpl = Generator.GenerateSerializeMethod <T>(il => Generator.EmitPrimitiveSerialize(typeof(T), il)); this.deserializeImpl = Generator.GenerateDeserializeMethod <T>(il => Generator.EmitPrimitiveDeserialize(typeof(T), il)); return(targetSchema ?? schema); }
static KnownSerializers() { UnserializableTypes.Add(typeof(Type)); UnserializableTypes.Add(typeof(IntPtr)); UnserializableTypes.Add(typeof(UIntPtr)); UnserializableTypes.Add(typeof(MemberInfo)); UnserializableTypes.Add(typeof(System.Diagnostics.StackTrace)); Default = new KnownSerializers(true, RuntimeInfo.Current); }
public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema) { var schemaMembers = new[] { new TypeMemberSchema("buffer", typeof(byte[]).AssemblyQualifiedName, true) }; var type = typeof(MemoryStream); var name = TypeSchema.GetContractName(type, serializers.RuntimeVersion); var schema = new TypeSchema(name, TypeSchema.GetId(name), type.AssemblyQualifiedName, TypeFlags.IsCollection, schemaMembers, SchemaVersion); return(targetSchema ?? schema); }
public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema) { this.elementHandler = serializers.GetHandler <T>(); // register element type var type = typeof(T[]); var name = TypeSchema.GetContractName(type, serializers.RuntimeVersion); var elementsMember = new TypeMemberSchema("Elements", typeof(T).AssemblyQualifiedName, true); var schema = new TypeSchema(name, TypeSchema.GetId(name), type.AssemblyQualifiedName, TypeFlags.IsCollection, new TypeMemberSchema[] { elementsMember }, Version); return(targetSchema ?? schema); }
public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema) { var runtimeSchema = TypeSchema.FromType(typeof(T), serializers.RuntimeVersion, this.GetType(), Version); var members = runtimeSchema.GetCompatibleMemberSet(targetSchema); this.serializeImpl = Generator.GenerateSerializeMethod <T>(il => Generator.EmitSerializeFields(typeof(T), serializers, il, members)); this.deserializeImpl = Generator.GenerateDeserializeMethod <T>(il => Generator.EmitDeserializeFields(typeof(T), serializers, il, members)); return(targetSchema ?? runtimeSchema); }
public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema) { if (targetSchema?.Version <= 2) { // maintain backward compatibility with older serialized data this.innerSerializer = new ClassSerializer <MemoryStream>(); } else { // otherwise default to the new implementation this.innerSerializer = new MemoryStreamSerializerImpl(); } return(this.innerSerializer.Initialize(serializers, targetSchema)); }
// ********************** // 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); }
/// <inheritdoc /> public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema) { // Add a comparerHandler of type IEqualityComparer. This will take care of serializing, // deserializing and cloning the Comparer member of Dictionary. Because the comparer field // is private, we will also need to generate a dynamic method so that we can set the // comparer field upon deserialization or cloning. This method should be invoked right after // clearing the target Dictionary, before adding the deserialized or cloned entries to it. this.comparerHandler = serializers.GetHandler <IEqualityComparer <TKey> >(); this.setComparerImpl = this.GenerateSetComparerMethod(); // Use an array serializer to serialize the dictionary elements as an array of key-value pairs this.entriesHandler = serializers.GetHandler <KeyValuePair <TKey, TValue>[]>(); var type = typeof(Dictionary <TKey, TValue>); var name = TypeSchema.GetContractName(type, serializers.RuntimeVersion); // Treat the Dictionary as a class with 2 members - a comparer and an array of key-value pairs var comparerMember = new TypeMemberSchema("Comparer", typeof(IEqualityComparer <TKey>).AssemblyQualifiedName, true); var entriesMember = new TypeMemberSchema("KeyValuePairs", typeof(KeyValuePair <TKey, TValue>[]).AssemblyQualifiedName, true); var schema = new TypeSchema(name, TypeSchema.GetId(name), type.AssemblyQualifiedName, TypeFlags.IsClass, new[] { comparerMember, entriesMember }, SchemaVersion); return(targetSchema ?? schema); }
// 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); }
public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema) { return(null); }
/// <summary> /// Initializes a new instance of the <see cref="SerializationContext"/> class, with the specified serialization overrides. /// </summary> /// <param name="serializers">The set of custom serializers to use instead of the default ones.</param> public SerializationContext(KnownSerializers serializers) { this.serializers = serializers ?? KnownSerializers.Default; }
public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema) { // schema is not used, since the behavior of arrays is hard-coded return(null); }
// ********************** // 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); }
public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema) { return(targetSchema ?? TypeSchema.FromType(typeof(string), serializers.RuntimeVersion, this.GetType(), Version)); }