Example #1
0
        /// <summary>
        /// Writes a primitive array to the stream.
        /// </summary>
        /// <typeparam name="T">The element type of the primitive array. Valid element types can be determined using <see cref="FormatterUtilities.IsPrimitiveArrayType(Type)" />.</typeparam>
        /// <param name="array">The primitive array to write.</param>
        /// <exception cref="System.ArgumentException">Type  + typeof(T).Name +  is not a valid primitive array type.</exception>
        /// <exception cref="System.ArgumentNullException">array</exception>
        public override void WritePrimitiveArray <T>(T[] array)
        {
            if (FormatterUtilities.IsPrimitiveArrayType(typeof(T)) == false)
            {
                throw new ArgumentException("Type " + typeof(T).Name + " is not a valid primitive array type.");
            }

            if (array == null)
            {
                throw new ArgumentNullException("array");
            }

            Action <string, T> writer = (Action <string, T>) this.primitiveTypeWriters[typeof(T)];

            this.WriteInt64(JsonConfig.PRIMITIVE_ARRAY_LENGTH_SIG, array.Length);
            this.WriteEntry(JsonConfig.PRIMITIVE_ARRAY_CONTENT_SIG, "[");
            this.forceNoSeparatorNextLine = true;
            this.PushArray();

            for (int i = 0; i < array.Length; i++)
            {
                writer(null, array[i]);
            }

            this.PopArray();
            this.StartNewLine(true);
            this.writer.Write("]");
        }
Example #2
0
        public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, out IFormatter formatter)
        {
            if (!type.IsArray)
            {
                formatter = null;
                return(false);
            }

            if (type.GetArrayRank() == 1)
            {
                if (FormatterUtilities.IsPrimitiveArrayType(type.GetElementType()))
                {
                    formatter = (IFormatter)Activator.CreateInstance(typeof(PrimitiveArrayFormatter <>).MakeGenericType(type.GetElementType()));
                }
                else
                {
                    formatter = (IFormatter)Activator.CreateInstance(typeof(ArrayFormatter <>).MakeGenericType(type.GetElementType()));
                }
            }
            else
            {
                formatter = (IFormatter)Activator.CreateInstance(typeof(MultiDimensionalArrayFormatter <,>).MakeGenericType(type, type.GetElementType()));
            }

            return(true);
        }
        /// <summary>
        /// Reads a primitive array value. This call will succeed if the next entry is an <see cref="EntryType.PrimitiveArray" />.
        /// <para />
        /// If the call fails (and returns <c>false</c>), it will skip the current entry value, unless that entry is an <see cref="EntryType.EndOfNode" /> or an <see cref="EntryType.EndOfArray" />.
        /// </summary>
        /// <typeparam name="T">The element type of the primitive array. Valid element types can be determined using <see cref="FormatterUtilities.IsPrimitiveArrayType(Type)" />.</typeparam>
        /// <param name="array">The resulting primitive array.</param>
        /// <returns>
        ///   <c>true</c> if reading a primitive array succeeded, otherwise <c>false</c>
        /// </returns>
        /// <exception cref="System.ArgumentException">Type  + typeof(T).Name +  is not a valid primitive array type.</exception>
        public override bool ReadPrimitiveArray <T>(out T[] array)
        {
            if (FormatterUtilities.IsPrimitiveArrayType(typeof(T)) == false)
            {
                throw new ArgumentException("Type " + typeof(T).Name + " is not a valid primitive array type.");
            }

            this.PeekEntry();

            if (this.peekedEntryType == EntryType.PrimitiveArray)
            {
                this.PushArray();

                if (this.peekedEntryName != JsonConfig.PRIMITIVE_ARRAY_LENGTH_SIG)
                {
                    this.Context.Config.DebugContext.LogError("Array entry wasn't preceded by an array length entry!");
                    array = null; // No array content for you!
                    return(false);
                }
                else
                {
                    int intLength;

                    if (int.TryParse(this.peekedEntryContent, NumberStyles.Any, CultureInfo.InvariantCulture, out intLength) == false)
                    {
                        this.Context.Config.DebugContext.LogError("Failed to parse array length: " + this.peekedEntryContent);
                        array = null; // No array content for you!
                        return(false);
                    }

                    this.ReadToNextEntry();

                    if (this.peekedEntryName != JsonConfig.PRIMITIVE_ARRAY_CONTENT_SIG)
                    {
                        this.Context.Config.DebugContext.LogError("Failed to find primitive array content entry after array length entry!");
                        array = null; // No array content for you!
                        return(false);
                    }

                    this.peekedEntryType = null;

                    Func <T> reader = (Func <T>) this.primitiveArrayReaders[typeof(T)];
                    array = new T[intLength];

                    for (int i = 0; i < intLength; i++)
                    {
                        array[i] = reader();
                    }

                    this.ExitArray();
                    return(true);
                }
            }
            else
            {
                this.SkipEntry();
                array = null;
                return(false);
            }
        }
Example #4
0
        /// <summary>
        /// Provides the actual implementation for serializing a value of type <see cref="T" />.
        /// </summary>
        /// <param name="value">The value to serialize.</param>
        /// <param name="writer">The writer to serialize with.</param>
        protected override void SerializeImplementation(ref T value, IDataWriter writer)
        {
            var members = FormatterUtilities.GetSerializableMembers(typeof(T), this.OverridePolicy ?? writer.Context.Config.SerializationPolicy);

            for (int i = 0; i < members.Length; i++)
            {
                var  member = members[i];
                Type type;
                var  memberValue = FormatterUtilities.GetMemberValue(member, value);

                if (object.ReferenceEquals(memberValue, null))
                {
                    type = FormatterUtilities.GetContainedType(member);
                }
                else
                {
                    type = memberValue.GetType();
                }

                var serializer = Serializer.Get(type);

                try
                {
                    serializer.WriteValueWeak(member.Name, memberValue, writer);
                }
                catch (Exception ex)
                {
                    writer.Context.Config.DebugContext.LogException(ex);
                }
            }
        }
        private static void EmitWriteMethodContents(
            ILGenerator gen,
            Type formattedType,
            MemberInfo[] members,
            Dictionary <Type, FieldBuilder> serializerFields,
            Dictionary <MemberInfo, List <string> > memberNames,
            Dictionary <Type, MethodInfo> serializerWriteMethods)
        {
            foreach (var member in members)
            {
                var memberType = FormatterUtilities.GetContainedType(member);

                gen.Emit(OpCodes.Ldsfld, serializerFields[memberType]);             // Load serializer instance for type
                gen.Emit(OpCodes.Ldstr, member.Name);                               // Load member name string

                // Now we load the value of the actual member
                if (member is FieldInfo)
                {
                    var fieldInfo = member as FieldInfo;

                    if (formattedType.IsValueType)
                    {
                        gen.Emit(OpCodes.Ldarg_0);                                  // Load value argument
                        gen.Emit(OpCodes.Ldfld, fieldInfo.DeAliasField());          // Load value of field
                    }
                    else
                    {
                        gen.Emit(OpCodes.Ldarg_0);                                  // Load value argument reference
                        gen.Emit(OpCodes.Ldind_Ref);                                // Indirectly load value of reference
                        gen.Emit(OpCodes.Ldfld, fieldInfo.DeAliasField());          // Load value of field
                    }
                }
                else if (member is PropertyInfo)
                {
                    var propInfo = member as PropertyInfo;

                    if (formattedType.IsValueType)
                    {
                        gen.Emit(OpCodes.Ldarg_0);                                                      // Load value argument
                        gen.Emit(OpCodes.Call, propInfo.DeAliasProperty().GetGetMethod(true));          // Call property getter
                    }
                    else
                    {
                        gen.Emit(OpCodes.Ldarg_0);                                                      // Load value argument reference
                        gen.Emit(OpCodes.Ldind_Ref);                                                    // Indirectly load value of reference
                        gen.Emit(OpCodes.Callvirt, propInfo.DeAliasProperty().GetGetMethod(true));      // Call property getter
                    }
                }
                else
                {
                    throw new NotImplementedException();
                }

                gen.Emit(OpCodes.Ldarg_1);                                          // Load writer argument
                gen.Emit(OpCodes.Callvirt, serializerWriteMethods[memberType]);     // Call Serializer.WriteValue(string name, T value, IDataWriter writer)
            }

            gen.Emit(OpCodes.Ret);                                                  // Return method
        }
        /// <summary>
        /// Reads a primitive array value. This call will succeed if the next entry is an <see cref="EntryType.PrimitiveArray" />.
        /// <para />
        /// If the call fails (and returns <c>false</c>), it will skip the current entry value, unless that entry is an <see cref="EntryType.EndOfNode" /> or an <see cref="EntryType.EndOfArray" />.
        /// </summary>
        /// <typeparam name="T">The element type of the primitive array. Valid element types can be determined using <see cref="FormatterUtilities.IsPrimitiveArrayType(Type)" />.</typeparam>
        /// <param name="array">The resulting primitive array.</param>
        /// <returns>
        ///   <c>true</c> if reading a primitive array succeeded, otherwise <c>false</c>
        /// </returns>
        /// <exception cref="System.ArgumentException">Type  + typeof(T).Name +  is not a valid primitive array type.</exception>
        public override bool ReadPrimitiveArray <T>(out T[] array)
        {
            if (FormatterUtilities.IsPrimitiveArrayType(typeof(T)) == false)
            {
                throw new ArgumentException("Type " + typeof(T).Name + " is not a valid primitive array type.");
            }

            if (this.peekedEntryType != EntryType.PrimitiveArray)
            {
                this.SkipEntry();
                array = null;
                return(false);
            }

            if (typeof(T) == typeof(byte))
            {
                array = (T[])(object)ProperBitConverter.HexStringToBytes(this.peekedEntryData);
                return(true);
            }
            else
            {
                this.PeekEntry();

                long length;

                if (this.peekedEntryType != EntryType.PrimitiveArray)
                {
                    this.Context.Config.DebugContext.LogError("Expected entry of type '" + EntryType.StartOfArray + "' when reading primitive array but got entry of type '" + this.peekedEntryType + "'.");
                    this.SkipEntry();
                    array = new T[0];
                    return(false);
                }

                if (!long.TryParse(this.peekedEntryData, NumberStyles.Any, CultureInfo.InvariantCulture, out length))
                {
                    this.Context.Config.DebugContext.LogError("Failed to parse primitive array length from entry data '" + this.peekedEntryData + "'.");
                    this.SkipEntry();
                    array = new T[0];
                    return(false);
                }

                this.ConsumeCurrentEntry();
                this.PushArray();

                array = new T[length];

                Func <T> reader = (Func <T>) this.primitiveTypeReaders[typeof(T)];

                for (int i = 0; i < length; i++)
                {
                    array[i] = reader();
                }

                this.ExitArray();
                return(true);
            }
        }
Example #7
0
        private static IFormatter CreateGenericFormatter(Type formattedType, ModuleBuilder moduleBuilder, ISerializationPolicy policy)
        {
            Dictionary <string, MemberInfo> serializableMembers = FormatterUtilities.GetSerializableMembersMap(formattedType, policy);

            if (serializableMembers.Count == 0)
            {
                return((IFormatter)Activator.CreateInstance(typeof(EmptyTypeFormatter <>).MakeGenericType(formattedType)));
            }

            string helperTypeName = moduleBuilder.Name + "." +
                                    formattedType.GetCompilableNiceFullName() + "___" +
                                    formattedType.Assembly.GetName().Name + "___FormatterHelper___" +
                                    System.Threading.Interlocked.Increment(ref helperFormatterNameId);

            Dictionary <Type, MethodInfo>   serializerReadMethods;
            Dictionary <Type, MethodInfo>   serializerWriteMethods;
            Dictionary <Type, FieldBuilder> serializerFields;
            FieldBuilder dictField;
            Dictionary <MemberInfo, List <string> > memberNames;

            BuildHelperType(
                moduleBuilder,
                helperTypeName,
                formattedType,
                serializableMembers,
                out serializerReadMethods,
                out serializerWriteMethods,
                out serializerFields,
                out dictField,
                out memberNames
                );

            Type     formatterType = typeof(RuntimeEmittedFormatter <>).MakeGenericType(formattedType);
            Delegate del1, del2;

            // Read
            {
                Type          readDelegateType    = typeof(ReadDataEntryMethodDelegate <>).MakeGenericType(formattedType);
                MethodInfo    readDataEntryMethod = formatterType.GetMethod("ReadDataEntry", Flags.InstanceAnyVisibility);
                DynamicMethod dynamicReadMethod   = new DynamicMethod("Dynamic_" + formattedType.GetCompilableNiceFullName(), null, readDataEntryMethod.GetParameters().Select(n => n.ParameterType).ToArray(), true);
                readDataEntryMethod.GetParameters().ForEach(n => dynamicReadMethod.DefineParameter(n.Position, n.Attributes, n.Name));
                EmitReadMethodContents(dynamicReadMethod.GetILGenerator(), formattedType, dictField, serializerFields, memberNames, serializerReadMethods);
                del1 = dynamicReadMethod.CreateDelegate(readDelegateType);
            }

            // Write
            {
                Type          writeDelegateType      = typeof(WriteDataEntriesMethodDelegate <>).MakeGenericType(formattedType);
                MethodInfo    writeDataEntriesMethod = formatterType.GetMethod("WriteDataEntries", Flags.InstanceAnyVisibility);
                DynamicMethod dynamicWriteMethod     = new DynamicMethod("Dynamic_Write_" + formattedType.GetCompilableNiceFullName(), null, writeDataEntriesMethod.GetParameters().Select(n => n.ParameterType).ToArray(), true);
                writeDataEntriesMethod.GetParameters().ForEach(n => dynamicWriteMethod.DefineParameter(n.Position + 1, n.Attributes, n.Name));
                EmitWriteMethodContents(dynamicWriteMethod.GetILGenerator(), formattedType, serializerFields, memberNames, serializerWriteMethods);
                del2 = dynamicWriteMethod.CreateDelegate(writeDelegateType);
            }

            return((IFormatter)Activator.CreateInstance(formatterType, del1, del2));
        }
        /// <summary>
        /// Writes a primitive array to the stream.
        /// </summary>
        /// <typeparam name="T">The element type of the primitive array. Valid element types can be determined using <see cref="FormatterUtilities.IsPrimitiveArrayType(Type)" />.</typeparam>
        /// <param name="array">The primitive array to write.</param>
        /// <exception cref="System.ArgumentException">Type  + typeof(T).Name +  is not a valid primitive array type.</exception>
        public override void WritePrimitiveArray <T>(T[] array)
        {
            if (FormatterUtilities.IsPrimitiveArrayType(typeof(T)) == false)
            {
                throw new ArgumentException("Type " + typeof(T).Name + " is not a valid primitive array type.");
            }

            int bytesPerElement = PrimitiveSizes[typeof(T)];
            int byteCount       = array.Length * bytesPerElement;

            // Write entry flag
            this.Stream.WriteByte((byte)BinaryEntryType.PrimitiveArray);

            // Write array length
            ProperBitConverter.GetBytes(this.buffer, 0, array.Length);
            this.Stream.Write(this.buffer, 0, 4);

            // Write size of an element in bytes
            ProperBitConverter.GetBytes(this.buffer, 0, bytesPerElement);
            this.Stream.Write(this.buffer, 0, 4);

            // Write the actual array content
            if (typeof(T) == typeof(byte))
            {
                // We can include a special case for byte arrays, as there's no need to copy that to a buffer
                var byteArray = (byte[])(object)array;
                this.Stream.Write(byteArray, 0, byteCount);
            }
            else
            {
                // Otherwise we copy to a buffer in order to write the entire array into the stream with one call
                using (var tempBuffer = Buffer <byte> .Claim(byteCount))
                {
                    if (BitConverter.IsLittleEndian)
                    {
                        // We always store in little endian, so we can do a direct memory mapping, which is a lot faster
                        UnsafeUtilities.MemoryCopy(array, tempBuffer.Array, byteCount, 0, 0);
                    }
                    else
                    {
                        // We have to convert each individual element to bytes, since the byte order has to be reversed
                        Action <byte[], int, T> toBytes = (Action <byte[], int, T>)PrimitiveGetBytesMethods[typeof(T)];
                        var b = tempBuffer.Array;

                        for (int i = 0; i < array.Length; i++)
                        {
                            toBytes(b, i * bytesPerElement, array[i]);
                        }
                    }

                    this.Stream.Write(tempBuffer.Array, 0, byteCount);
                }
            }
        }
Example #9
0
        /// <summary>
        /// Provides the actual implementation for deserializing a value of type <see cref="T" />.
        /// </summary>
        /// <param name="value">The uninitialized value to serialize into. This value will have been created earlier using <see cref="BaseFormatter{T}.GetUninitializedObject" />.</param>
        /// <param name="reader">The reader to deserialize with.</param>
        protected override void DeserializeImplementation(ref T value, IDataReader reader)
        {
            // We sadly *must* box so that complex value types get their values properly set by reflection.
            // At least we only box these once.
            object boxedValue = value;

            var members = FormatterUtilities.GetSerializableMembersMap(typeof(T), this.OverridePolicy ?? reader.Context.Config.SerializationPolicy);

            EntryType entryType;
            string    name;

            while ((entryType = reader.PeekEntry(out name)) != EntryType.EndOfNode && entryType != EntryType.EndOfArray && entryType != EntryType.EndOfStream)
            {
                if (string.IsNullOrEmpty(name))
                {
                    reader.Context.Config.DebugContext.LogError("Entry of type \"" + entryType + "\" in node \"" + reader.CurrentNodeName + "\" is missing a name.");
                    reader.SkipEntry();
                    continue;
                }

                MemberInfo member;

                if (members.TryGetValue(name, out member) == false)
                {
                    reader.Context.Config.DebugContext.LogWarning("Lost serialization data for entry \"" + name + "\" of type \"" + entryType + "\"in node \"" + reader.CurrentNodeName + "\".");
                    reader.SkipEntry();
                    continue;
                }

                Type expectedType = FormatterUtilities.GetContainedType(member);

                try
                {
                    var    serializer = Serializer.Get(expectedType);
                    object entryValue = serializer.ReadValueWeak(reader);
                    FormatterUtilities.SetMemberValue(member, boxedValue, entryValue);
                }
                catch (Exception ex)
                {
                    reader.Context.Config.DebugContext.LogException(ex);
                }
            }

            value = (T)boxedValue; // Unbox
        }
        /// <summary>
        /// Not yet documented.
        /// </summary>
        public override void WritePrimitiveArray <T>(T[] array)
        {
            if (FormatterUtilities.IsPrimitiveArrayType(typeof(T)) == false)
            {
                throw new ArgumentException("Type " + typeof(T).Name + " is not a valid primitive array type.");
            }

            if (typeof(T) == typeof(byte))
            {
                string hex = ProperBitConverter.BytesToHexString((byte[])(object)array);

                this.Nodes.Add(new SerializationNode()
                {
                    Name  = string.Empty,
                    Entry = EntryType.PrimitiveArray,
                    Data  = hex
                });
            }
            else
            {
                this.Nodes.Add(new SerializationNode()
                {
                    Name  = string.Empty,
                    Entry = EntryType.PrimitiveArray,
                    Data  = array.LongLength.ToString(CultureInfo.InvariantCulture)
                });

                this.PushArray();

                Action <string, T> writer = (Action <string, T>) this.primitiveTypeWriters[typeof(T)];

                for (int i = 0; i < array.Length; i++)
                {
                    writer(string.Empty, array[i]);
                }

                this.EndArrayNode();
            }
        }
Example #11
0
        /// <summary>
        /// Skips the next entry value, unless it is an <see cref="EntryType.EndOfNode"/> or an <see cref="EntryType.EndOfArray"/>. If the next entry value is an <see cref="EntryType.StartOfNode"/> or an <see cref="EntryType.StartOfArray"/>, all of its contents will be processed, deserialized and registered in the deserialization context, so that internal reference values are not lost to entries further down the stream.
        /// </summary>
        public virtual void SkipEntry()
        {
            var peekedEntry = this.PeekEntry();

            if (peekedEntry == EntryType.StartOfNode)
            {
                Type type;

                bool exitNode = true;

                this.EnterNode(out type);

                try
                {
                    if (type != null)
                    {
                        // We have the necessary metadata to read this type, and register all of its reference values (perhaps including itself) in the serialization context
                        // Sadly, we have no choice but to risk boxing of structs here
                        // Luckily, this is a rare case

                        if (FormatterUtilities.IsPrimitiveType(type))
                        {
                            // It is a boxed primitive type; we read the value and register it
                            var    serializer = Serializer.Get(type);
                            object value      = serializer.ReadValueWeak(this);

                            if (this.CurrentNodeId >= 0)
                            {
                                this.Context.RegisterInternalReference(this.CurrentNodeId, value);
                            }
                        }
                        else
                        {
                            var    formatter = FormatterLocator.GetFormatter(type, this.Context.Config.SerializationPolicy);
                            object value     = formatter.Deserialize(this);

                            if (this.CurrentNodeId >= 0)
                            {
                                this.Context.RegisterInternalReference(this.CurrentNodeId, value);
                            }
                        }
                    }
                    else
                    {
                        // We have no metadata, and reference values might be lost
                        // We must read until a node on the same level terminates
                        while (true)
                        {
                            peekedEntry = this.PeekEntry();

                            if (peekedEntry == EntryType.EndOfStream)
                            {
                                break;
                            }
                            else if (peekedEntry == EntryType.EndOfNode)
                            {
                                break;
                            }
                            else if (peekedEntry == EntryType.EndOfArray)
                            {
                                this.ReadToNextEntry(); // Consume end of arrays that we can potentially get stuck on
                            }
                            else
                            {
                                this.SkipEntry();
                            }
                        }
                    }
                }
                catch (SerializationAbortException ex)
                {
                    exitNode = false;
                    throw ex;
                }
                finally
                {
                    if (exitNode)
                    {
                        this.ExitNode();
                    }
                }
            }
            else if (peekedEntry == EntryType.StartOfArray)
            {
                // We must read until an array on the same level terminates
                this.ReadToNextEntry(); // Consume start of array

                while (true)
                {
                    peekedEntry = this.PeekEntry();

                    if (peekedEntry == EntryType.EndOfStream)
                    {
                        break;
                    }
                    else if (peekedEntry == EntryType.EndOfArray)
                    {
                        this.ReadToNextEntry(); // Consume end of array and break
                        break;
                    }
                    else if (peekedEntry == EntryType.EndOfNode)
                    {
                        this.ReadToNextEntry(); // Consume end of nodes that we can potentially get stuck on
                    }
                    else
                    {
                        this.SkipEntry();
                    }
                }
            }
            else if (peekedEntry != EntryType.EndOfArray && peekedEntry != EntryType.EndOfNode) // We can't skip end of arrays and end of nodes
            {
                this.ReadToNextEntry();                                                         // We can just skip a single value entry
            }
        }
Example #12
0
        private static Serializer Create(Type type)
        {
            try
            {
                Type resultType = null;

                if (type.IsEnum)
                {
                    resultType = typeof(EnumSerializer <>).MakeGenericType(type);
                }
                else if (FormatterUtilities.IsPrimitiveType(type))
                {
                    try
                    {
                        resultType = PrimitiveReaderWriterTypes[type];
                    }
                    catch (KeyNotFoundException)
                    {
                        UnityEngine.Debug.LogError("Failed to find primitive serializer for " + type.Name);
                    }
                }
                else
                {
                    resultType = typeof(ComplexTypeSerializer <>).MakeGenericType(type);
                }

                return((Serializer)Activator.CreateInstance(resultType));
            }
            // System.ExecutionEngineException is marked obsolete in .NET 4.6
            // That's all very good for .NET, but Unity still uses it!
#pragma warning disable 618
            catch (TargetInvocationException ex)
            {
                if (ex.GetBaseException() is ExecutionEngineException)
                {
                    LogAOTError(type, ex.GetBaseException() as ExecutionEngineException);
                    return(null);
                }
                else
                {
                    throw ex;
                }
            }
            catch (TypeInitializationException ex)
            {
                if (ex.GetBaseException() is ExecutionEngineException)
                {
                    LogAOTError(type, ex.GetBaseException() as ExecutionEngineException);
                    return(null);
                }
                else
                {
                    throw ex;
                }
            }
            catch (ExecutionEngineException ex)
            {
                LogAOTError(type, ex);
                return(null);
            }
        }
Example #13
0
        internal static List <IFormatter> GetAllCompatiblePredefinedFormatters(Type type, ISerializationPolicy policy)
        {
            if (FormatterUtilities.IsPrimitiveType(type))
            {
                throw new ArgumentException("Cannot create formatters for a primitive type like " + type.Name);
            }

            List <IFormatter> formatters = new List <IFormatter>();

            // First call formatter locators before checking for registered formatters
            for (int i = 0; i < FormatterLocators.Count; i++)
            {
                try
                {
                    IFormatter result;
                    if (FormatterLocators[i].LocatorInstance.TryGetFormatter(type, FormatterLocationStep.BeforeRegisteredFormatters, policy, out result))
                    {
                        formatters.Add(result);
                    }
                }
                catch (TargetInvocationException ex)
                {
                    throw ex;
                }
                catch (TypeInitializationException ex)
                {
                    throw ex;
                }
#pragma warning disable CS0618 // Type or member is obsolete
                catch (ExecutionEngineException ex)
#pragma warning restore CS0618 // Type or member is obsolete
                {
                    throw ex;
                }
                catch (Exception ex)
                {
                    Debug.LogException(new Exception("Exception was thrown while calling FormatterLocator " + FormatterLocators[i].GetType().FullName + ".", ex));
                }
            }

            // Then check for valid registered formatters
            for (int i = 0; i < FormatterInfos.Count; i++)
            {
                var info = FormatterInfos[i];

                Type formatterType = null;

                if (type == info.TargetType)
                {
                    formatterType = info.FormatterType;
                }
                else if (info.FormatterType.IsGenericType && info.TargetType.IsGenericParameter)
                {
                    Type[] inferredArgs;

                    if (info.FormatterType.TryInferGenericParameters(out inferredArgs, type))
                    {
                        formatterType = info.FormatterType.GetGenericTypeDefinition().MakeGenericType(inferredArgs);
                    }
                }
                else if (type.IsGenericType && info.FormatterType.IsGenericType && info.TargetType.IsGenericType && type.GetGenericTypeDefinition() == info.TargetType.GetGenericTypeDefinition())
                {
                    Type[] args = type.GetGenericArguments();

                    if (info.FormatterType.AreGenericConstraintsSatisfiedBy(args))
                    {
                        formatterType = info.FormatterType.GetGenericTypeDefinition().MakeGenericType(args);
                    }
                }

                if (formatterType != null)
                {
                    var instance = GetFormatterInstance(formatterType);

                    if (instance == null)
                    {
                        continue;
                    }

                    if (info.AskIfCanFormatTypes && !((IAskIfCanFormatTypes)instance).CanFormatType(type))
                    {
                        continue;
                    }

                    formatters.Add(instance);
                }
            }

            // Then call formatter locators after checking for registered formatters
            for (int i = 0; i < FormatterLocators.Count; i++)
            {
                try
                {
                    IFormatter result;
                    if (FormatterLocators[i].LocatorInstance.TryGetFormatter(type, FormatterLocationStep.AfterRegisteredFormatters, policy, out result))
                    {
                        formatters.Add(result);
                    }
                }
                catch (TargetInvocationException ex)
                {
                    throw ex;
                }
                catch (TypeInitializationException ex)
                {
                    throw ex;
                }
#pragma warning disable CS0618 // Type or member is obsolete
                catch (ExecutionEngineException ex)
#pragma warning restore CS0618 // Type or member is obsolete
                {
                    throw ex;
                }
                catch (Exception ex)
                {
                    Debug.LogException(new Exception("Exception was thrown while calling FormatterLocator " + FormatterLocators[i].GetType().FullName + ".", ex));
                }
            }

            formatters.Add((IFormatter)Activator.CreateInstance(typeof(ReflectionFormatter <>).MakeGenericType(type)));

            return(formatters);
        }
        private static void EmitReadMethodContents(
            ILGenerator gen,
            Type formattedType,
            FieldInfo dictField,
            Dictionary <Type, FieldBuilder> serializerFields,
            Dictionary <MemberInfo, List <string> > memberNames,
            Dictionary <Type, MethodInfo> serializerReadMethods)
        {
            MethodInfo skipMethod        = typeof(IDataReader).GetMethod("SkipEntry", Flags.InstancePublic);
            MethodInfo tryGetValueMethod = typeof(Dictionary <string, int>).GetMethod("TryGetValue", Flags.InstancePublic);

            //methodBuilder.DefineParameter(5, ParameterAttributes.None, "switchLookup");

            LocalBuilder lookupResult = gen.DeclareLocal(typeof(int));

            Label defaultLabel = gen.DefineLabel();
            Label switchLabel  = gen.DefineLabel();
            Label endLabel     = gen.DefineLabel();

            Label[] switchLabels = memberNames.Select(n => gen.DefineLabel()).ToArray();

            gen.Emit(OpCodes.Ldarg_1);                                              // Load entryName string
            gen.Emit(OpCodes.Ldnull);                                               // Load null
            gen.Emit(OpCodes.Ceq);                                                  // Equality check
            gen.Emit(OpCodes.Brtrue, defaultLabel);                                 // If entryName is null, go to default case

            //gen.Emit(OpCodes.Ldarg, (short)4);                                      // Load lookup dictionary argument (OLD CODE)

            gen.Emit(OpCodes.Ldsfld, dictField);                                    // Load lookup dictionary from static field on helper type
            gen.Emit(OpCodes.Ldarg_1);                                              // Load entryName string
            gen.Emit(OpCodes.Ldloca, (short)lookupResult.LocalIndex);               // Load address of lookupResult
            gen.Emit(OpCodes.Callvirt, tryGetValueMethod);                          // Call TryGetValue on the dictionary

            gen.Emit(OpCodes.Brtrue, switchLabel);                                  // If TryGetValue returned true, go to the switch case
            gen.Emit(OpCodes.Br, defaultLabel);                                     // Else, go to the default case

            gen.MarkLabel(switchLabel);                                             // Switch starts here
            gen.Emit(OpCodes.Ldloc, lookupResult);                                  // Load lookupResult
            gen.Emit(OpCodes.Switch, switchLabels);                                 // Perform switch on switchLabels

            int count = 0;

            foreach (var member in memberNames.Keys)
            {
                var memberType = FormatterUtilities.GetContainedType(member);

                var propInfo  = member as PropertyInfo;
                var fieldInfo = member as FieldInfo;

                gen.MarkLabel(switchLabels[count]);                                 // Switch case for [count] starts here

                // Now we load the instance that we have to set the value on
                gen.Emit(OpCodes.Ldarg_0);                                          // Load value reference

                if (formattedType.IsValueType == false)
                {
                    gen.Emit(OpCodes.Ldind_Ref);                                    // Indirectly load value of reference
                }

                // Now we deserialize the value itself
                gen.Emit(OpCodes.Ldsfld, serializerFields[memberType]);             // Load serializer from serializer container type
                gen.Emit(OpCodes.Ldarg, (short)3);                                  // Load reader argument
                gen.Emit(OpCodes.Callvirt, serializerReadMethods[memberType]);      // Call Serializer.ReadValue(IDataReader reader)

                // The stack now contains the formatted instance and the deserialized value to set the member to
                // Now we set the value
                if (fieldInfo != null)
                {
                    gen.Emit(OpCodes.Stfld, fieldInfo.DeAliasField());                              // Set field
                }
                else if (propInfo != null)
                {
                    gen.Emit(OpCodes.Callvirt, propInfo.DeAliasProperty().GetSetMethod(true));      // Call property setter
                }
                else
                {
                    throw new NotImplementedException();
                }

                gen.Emit(OpCodes.Br, endLabel);                                     // Jump to end of method

                count++;
            }

            gen.MarkLabel(defaultLabel);                                            // Default case starts here
            gen.Emit(OpCodes.Ldarg, (short)3);                                      // Load reader argument
            gen.Emit(OpCodes.Callvirt, skipMethod);                                 // Call IDataReader.SkipEntry

            gen.MarkLabel(endLabel);                                                // Method end starts here
            gen.Emit(OpCodes.Ret);                                                  // Return method
        }
        private static Type BuildHelperType(
            ModuleBuilder moduleBuilder,
            string helperTypeName,
            Type formattedType,
            Dictionary <string, MemberInfo> serializableMembers,
            out Dictionary <Type, MethodInfo> serializerReadMethods,
            out Dictionary <Type, MethodInfo> serializerWriteMethods,
            out Dictionary <Type, FieldBuilder> serializerFields,
            out FieldBuilder dictField,
            out Dictionary <MemberInfo, List <string> > memberNames)
        {
            TypeBuilder helperTypeBuilder = moduleBuilder.DefineType(helperTypeName, TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class);

            memberNames = new Dictionary <MemberInfo, List <string> >();

            foreach (var entry in serializableMembers)
            {
                List <string> list;

                if (memberNames.TryGetValue(entry.Value, out list) == false)
                {
                    list = new List <string>();
                    memberNames.Add(entry.Value, list);
                }

                list.Add(entry.Key);
            }

            dictField = helperTypeBuilder.DefineField("SwitchLookup", typeof(Dictionary <string, int>), FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.InitOnly);

            List <Type> neededSerializers = memberNames.Keys.Select(n => FormatterUtilities.GetContainedType(n)).Distinct().ToList();

            serializerReadMethods  = new Dictionary <Type, MethodInfo>(neededSerializers.Count);
            serializerWriteMethods = new Dictionary <Type, MethodInfo>(neededSerializers.Count);
            serializerFields       = new Dictionary <Type, FieldBuilder>(neededSerializers.Count);

            foreach (var t in neededSerializers)
            {
                string name    = t.GetCompilableNiceFullName() + "__Serializer";
                int    counter = 1;

                while (serializerFields.Values.Any(n => n.Name == name))
                {
                    counter++;
                    name = t.GetCompilableNiceFullName() + "__Serializer" + counter;
                }

                Type serializerType = typeof(Serializer <>).MakeGenericType(t);

                serializerReadMethods.Add(t, serializerType.GetMethod("ReadValue", Flags.InstancePublicDeclaredOnly));
                serializerWriteMethods.Add(t, serializerType.GetMethod("WriteValue", Flags.InstancePublicDeclaredOnly, null, new[] { typeof(string), t, typeof(IDataWriter) }, null));
                serializerFields.Add(t, helperTypeBuilder.DefineField(name, serializerType, FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.InitOnly));
            }

            //FieldBuilder readMethodFieldBuilder = helperTypeBuilder.DefineField("ReadMethod", readDelegateType, FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.InitOnly);
            //FieldBuilder writeMethodFieldBuilder = helperTypeBuilder.DefineField("WriteMethod", writeDelegateType, FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.InitOnly);

            // We generate a static constructor for our formatter helper type that initializes our switch lookup dictionary and our needed Serializer references
            {
                var addMethod             = typeof(Dictionary <string, int>).GetMethod("Add", Flags.InstancePublic);
                var dictionaryConstructor = typeof(Dictionary <string, int>).GetConstructor(Type.EmptyTypes);
                var serializerGetMethod   = typeof(Serializer).GetMethod("Get", Flags.StaticPublic, null, new[] { typeof(Type) }, null);
                var typeOfMethod          = typeof(Type).GetMethod("GetTypeFromHandle", Flags.StaticPublic, null, new Type[] { typeof(RuntimeTypeHandle) }, null);

                ConstructorBuilder staticConstructor = helperTypeBuilder.DefineTypeInitializer();
                ILGenerator        gen = staticConstructor.GetILGenerator();

                gen.Emit(OpCodes.Newobj, dictionaryConstructor);                        // Create new dictionary

                int count = 0;

                foreach (var entry in memberNames)
                {
                    foreach (var name in entry.Value)
                    {
                        gen.Emit(OpCodes.Dup);                                          // Load duplicate dictionary value
                        gen.Emit(OpCodes.Ldstr, name);                                  // Load entry name
                        gen.Emit(OpCodes.Ldc_I4, count);                                // Load entry index
                        gen.Emit(OpCodes.Call, addMethod);                              // Call dictionary add
                    }

                    count++;
                }

                gen.Emit(OpCodes.Stsfld, dictField);                                    // Set static dictionary field to dictionary value

                foreach (var entry in serializerFields)
                {
                    gen.Emit(OpCodes.Ldtoken, entry.Key);                               // Load type token
                    gen.Emit(OpCodes.Call, typeOfMethod);                               // Call typeof method (this pushes a type value onto the stack)
                    gen.Emit(OpCodes.Call, serializerGetMethod);                        // Call Serializer.Get(Type type) method
                    gen.Emit(OpCodes.Stsfld, entry.Value);                              // Set static serializer field to result of get method
                }

                gen.Emit(OpCodes.Ret);                                                  // Return
            }

            // Now we need to actually create the serializer container type so we can generate the dynamic methods below without getting TypeLoadExceptions up the wazoo
            return(helperTypeBuilder.CreateType());
        }
        /// <summary>
        /// Emits a formatter for a given type into a given module builder, using a given serialization policy to determine which members to serialize.
        /// </summary>
        /// <param name="formattedType">Type to create a formatter for.</param>
        /// <param name="moduleBuilder">The module builder to emit a formatter into.</param>
        /// <param name="policy">The serialization policy to use for creating the formatter.</param>
        /// <returns>The fully constructed, emitted formatter type.</returns>
        public static Type EmitAOTFormatter(Type formattedType, ModuleBuilder moduleBuilder, ISerializationPolicy policy)
        {
            Dictionary <string, MemberInfo> serializableMembers = FormatterUtilities.GetSerializableMembersMap(formattedType, policy);

            string formatterName       = moduleBuilder.Name + "." + formattedType.GetCompilableNiceFullName() + "__AOTFormatter";
            string formatterHelperName = moduleBuilder.Name + "." + formattedType.GetCompilableNiceFullName() + "__FormatterHelper";

            if (serializableMembers.Count == 0)
            {
                return(moduleBuilder.DefineType(
                           formatterName,
                           TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class,
                           typeof(EmptyAOTEmittedFormatter <>).MakeGenericType(formattedType)
                           ).CreateType());
            }

            Dictionary <Type, MethodInfo>   serializerReadMethods;
            Dictionary <Type, MethodInfo>   serializerWriteMethods;
            Dictionary <Type, FieldBuilder> serializerFields;
            FieldBuilder dictField;
            Dictionary <MemberInfo, List <string> > memberNames;

            BuildHelperType(
                moduleBuilder,
                formatterHelperName,
                formattedType,
                serializableMembers,
                out serializerReadMethods,
                out serializerWriteMethods,
                out serializerFields,
                out dictField,
                out memberNames
                );

            TypeBuilder formatterType = moduleBuilder.DefineType(
                formatterName,
                TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class,
                typeof(AOTEmittedFormatter <>).MakeGenericType(formattedType)
                );

            // Read
            {
                MethodInfo readBaseMethod = formatterType.BaseType.GetMethod("ReadDataEntry", Flags.InstanceAnyVisibility);

                MethodBuilder readMethod = formatterType.DefineMethod(
                    readBaseMethod.Name,
                    MethodAttributes.Family | MethodAttributes.Virtual,
                    readBaseMethod.ReturnType,
                    readBaseMethod.GetParameters().Select(n => n.ParameterType).ToArray()
                    );

                readBaseMethod.GetParameters().ForEach(n => readMethod.DefineParameter(n.Position, n.Attributes, n.Name));
                EmitReadMethodContents(readMethod.GetILGenerator(), formattedType, dictField, serializerFields, memberNames, serializerReadMethods);
            }

            // Write
            {
                MethodInfo writeBaseMethod = formatterType.BaseType.GetMethod("WriteDataEntries", Flags.InstanceAnyVisibility);

                MethodBuilder dynamicWriteMethod = formatterType.DefineMethod(
                    writeBaseMethod.Name,
                    MethodAttributes.Family | MethodAttributes.Virtual,
                    writeBaseMethod.ReturnType,
                    writeBaseMethod.GetParameters().Select(n => n.ParameterType).ToArray()
                    );

                writeBaseMethod.GetParameters().ForEach(n => dynamicWriteMethod.DefineParameter(n.Position + 1, n.Attributes, n.Name));
                EmitWriteMethodContents(dynamicWriteMethod.GetILGenerator(), formattedType, serializerFields, memberNames, serializerWriteMethods);
            }

            var result = formatterType.CreateType();

            // Register the formatter on the assembly
            ((AssemblyBuilder)moduleBuilder.Assembly).SetCustomAttribute(new CustomAttributeBuilder(typeof(RegisterFormatterAttribute).GetConstructor(new Type[] { typeof(Type), typeof(int) }), new object[] { formatterType, -1 }));

            return(result);
        }
        /// <summary>
        /// Reads a value of type <see cref="T" />.
        /// </summary>
        /// <param name="reader">The reader to use.</param>
        /// <returns>
        /// The value which has been read.
        /// </returns>
        public override T ReadValue(IDataReader reader)
        {
            var context = reader.Context;

            if (context.Config.SerializationPolicy.AllowNonSerializableTypes == false && TypeOf_T.IsSerializable == false)
            {
                context.Config.DebugContext.LogError("The type " + TypeOf_T.Name + " is not marked as serializable.");
                return(default(T));
            }

            bool exitNode = true;

            string name;
            var    entry = reader.PeekEntry(out name);

            if (ComplexTypeIsValueType)
            {
                if (entry == EntryType.Null)
                {
                    context.Config.DebugContext.LogWarning("Expecting complex struct of type " + TypeOf_T.GetNiceFullName() + " but got null value.");
                    reader.ReadNull();
                    return(default(T));
                }
                else if (entry != EntryType.StartOfNode)
                {
                    context.Config.DebugContext.LogWarning("Unexpected entry '" + name + "' of type " + entry.ToString() + ", when " + EntryType.StartOfNode + " was expected. A value has likely been lost.");
                    reader.SkipEntry();
                    return(default(T));
                }

                try
                {
                    Type expectedType = TypeOf_T;
                    Type serializedType;

                    if (reader.EnterNode(out serializedType))
                    {
                        if (serializedType != expectedType)
                        {
                            if (serializedType != null)
                            {
                                context.Config.DebugContext.LogWarning("Expected complex struct value " + expectedType.Name + " but the serialized value is of type " + serializedType.Name + ".");

                                if (serializedType.IsCastableTo(expectedType))
                                {
                                    object value = FormatterLocator.GetFormatter(serializedType, context.Config.SerializationPolicy).Deserialize(reader);

                                    bool serializedTypeIsNullable = serializedType.IsGenericType && serializedType.GetGenericTypeDefinition() == typeof(Nullable <>);
                                    bool allowCastMethod          = !ComplexTypeIsNullable && !serializedTypeIsNullable;

                                    var castMethod = allowCastMethod ? serializedType.GetCastMethodDelegate(expectedType) : null;

                                    if (castMethod != null)
                                    {
                                        return((T)castMethod(value));
                                    }
                                    else
                                    {
                                        return((T)value);
                                    }
                                }
                                else if (AllowDeserializeInvalidDataForT || reader.Context.Config.AllowDeserializeInvalidData)
                                {
                                    context.Config.DebugContext.LogWarning("Can't cast serialized type " + serializedType.Name + " into expected type " + expectedType.Name + ". Attempting to deserialize with possibly invalid data. Value may be lost or corrupted for node '" + name + "'.");
                                    return(GetBaseFormatter(context.Config.SerializationPolicy).Deserialize(reader));
                                }
                                else
                                {
                                    context.Config.DebugContext.LogWarning("Can't cast serialized type " + serializedType.Name + " into expected type " + expectedType.Name + ". Value lost for node '" + name + "'.");
                                    return(default(T));
                                }
                            }
                            else if (AllowDeserializeInvalidDataForT || reader.Context.Config.AllowDeserializeInvalidData)
                            {
                                context.Config.DebugContext.LogWarning("Expected complex struct value " + expectedType.Name + " but the serialized type could not be resolved. Attempting to deserialize with possibly invalid data. Value may be lost or corrupted for node '" + name + "'.");
                                return(GetBaseFormatter(context.Config.SerializationPolicy).Deserialize(reader));
                            }
                            else
                            {
                                context.Config.DebugContext.LogWarning("Expected complex struct value " + expectedType.Name + " but the serialized type could not be resolved. Value lost for node '" + name + "'.");
                                return(default(T));
                            }
                        }
                        else
                        {
                            return(GetBaseFormatter(context.Config.SerializationPolicy).Deserialize(reader));
                        }
                    }
                    else
                    {
                        context.Config.DebugContext.LogError("Failed to enter node '" + name + "'.");
                        return(default(T));
                    }
                }
                catch (SerializationAbortException ex)
                {
                    exitNode = false;
                    throw ex;
                }
                catch (Exception ex)
                {
                    context.Config.DebugContext.LogException(ex);
                    return(default(T));
                }
                finally
                {
                    if (exitNode)
                    {
                        reader.ExitNode();
                    }
                }
            }
            else
            {
                switch (entry)
                {
                case EntryType.Null:
                {
                    reader.ReadNull();
                    return(default(T));
                }

                case EntryType.ExternalReferenceByIndex:
                {
                    int index;
                    reader.ReadExternalReference(out index);

                    object value = context.GetExternalObject(index);

                    try
                    {
                        return((T)value);
                    }
                    catch (InvalidCastException)
                    {
                        context.Config.DebugContext.LogWarning("Can't cast external reference type " + value.GetType().Name + " into expected type " + TypeOf_T.Name + ". Value lost for node '" + name + "'.");
                        return(default(T));
                    }
                }

                case EntryType.ExternalReferenceByGuid:
                {
                    Guid guid;
                    reader.ReadExternalReference(out guid);

                    object value = context.GetExternalObject(guid);

                    try
                    {
                        return((T)value);
                    }
                    catch (InvalidCastException)
                    {
                        context.Config.DebugContext.LogWarning("Can't cast external reference type " + value.GetType().Name + " into expected type " + TypeOf_T.Name + ". Value lost for node '" + name + "'.");
                        return(default(T));
                    }
                }

                case EntryType.ExternalReferenceByString:
                {
                    string id;
                    reader.ReadExternalReference(out id);

                    object value = context.GetExternalObject(id);

                    try
                    {
                        return((T)value);
                    }
                    catch (InvalidCastException)
                    {
                        context.Config.DebugContext.LogWarning("Can't cast external reference type " + value.GetType().Name + " into expected type " + TypeOf_T.Name + ". Value lost for node '" + name + "'.");
                        return(default(T));
                    }
                }

                case EntryType.InternalReference:
                {
                    int id;
                    reader.ReadInternalReference(out id);

                    object value = context.GetInternalReference(id);

                    try
                    {
                        return((T)value);
                    }
                    catch (InvalidCastException)
                    {
                        context.Config.DebugContext.LogWarning("Can't cast internal reference type " + value.GetType().Name + " into expected type " + TypeOf_T.Name + ". Value lost for node '" + name + "'.");
                        return(default(T));
                    }
                }

                case EntryType.StartOfNode:
                {
                    try
                    {
                        Type expectedType = TypeOf_T;
                        Type serializedType;
                        int  id;

                        if (reader.EnterNode(out serializedType))
                        {
                            id = reader.CurrentNodeId;

                            T result;

                            if (serializedType != null && expectedType != serializedType)         // We have type metadata different from the expected type
                            {
                                bool success     = false;
                                var  isPrimitive = FormatterUtilities.IsPrimitiveType(serializedType);

                                bool assignableCast;

                                if (ComplexTypeMayBeBoxedValueType && isPrimitive)
                                {
                                    // It's a boxed primitive type, so simply read that straight and register success
                                    var serializer = Serializer.Get(serializedType);
                                    result  = (T)serializer.ReadValueWeak(reader);
                                    success = true;
                                }
                                else if ((assignableCast = expectedType.IsAssignableFrom(serializedType)) || serializedType.HasCastDefined(expectedType, false))
                                {
                                    try
                                    {
                                        object value;

                                        if (isPrimitive)
                                        {
                                            var serializer = Serializer.Get(serializedType);
                                            value = serializer.ReadValueWeak(reader);
                                        }
                                        else
                                        {
                                            var alternateFormatter = FormatterLocator.GetFormatter(serializedType, context.Config.SerializationPolicy);
                                            value = alternateFormatter.Deserialize(reader);
                                        }

                                        if (assignableCast)
                                        {
                                            result = (T)value;
                                        }
                                        else
                                        {
                                            var castMethod = serializedType.GetCastMethodDelegate(expectedType);

                                            if (castMethod != null)
                                            {
                                                result = (T)castMethod(value);
                                            }
                                            else
                                            {
                                                // Let's just give it a go anyways
                                                result = (T)value;
                                            }
                                        }

                                        success = true;
                                    }
                                    catch (SerializationAbortException ex)
                                    {
                                        exitNode = false;
                                        throw ex;
                                    }
                                    catch (InvalidCastException)
                                    {
                                        success = false;
                                        result  = default(T);
                                    }
                                }
                                else if (!ComplexTypeIsAbstract && (AllowDeserializeInvalidDataForT || reader.Context.Config.AllowDeserializeInvalidData))
                                {
                                    // We will try to deserialize an instance of T with the invalid data.
                                    context.Config.DebugContext.LogWarning("Can't cast serialized type " + serializedType.Name + " into expected type " + expectedType.Name + ". Attempting to deserialize with invalid data. Value may be lost or corrupted for node '" + name + "'.");
                                    result  = GetBaseFormatter(context.Config.SerializationPolicy).Deserialize(reader);
                                    success = true;
                                }
                                else
                                {
                                    // We couldn't cast or use the type, but we still have to deserialize it and register
                                    // the reference so the reference isn't lost if it is referred to further down
                                    // the data stream.

                                    var    alternateFormatter = FormatterLocator.GetFormatter(serializedType, context.Config.SerializationPolicy);
                                    object value = alternateFormatter.Deserialize(reader);

                                    if (id >= 0)
                                    {
                                        context.RegisterInternalReference(id, value);
                                    }

                                    result = default(T);
                                }

                                if (!success)
                                {
                                    // We can't use this
                                    context.Config.DebugContext.LogWarning("Can't cast serialized type " + serializedType.Name + " into expected type " + expectedType.Name + ". Value lost for node '" + name + "'.");
                                    result = default(T);
                                }
                            }
                            else if (ComplexTypeIsAbstract)
                            {
                                result = default(T);
                            }
                            else
                            {
                                result = GetBaseFormatter(context.Config.SerializationPolicy).Deserialize(reader);
                            }

                            if (id >= 0)
                            {
                                context.RegisterInternalReference(id, result);
                            }

                            return(result);
                        }
                        else
                        {
                            context.Config.DebugContext.LogError("Failed to enter node '" + name + "'.");
                            return(default(T));
                        }
                    }
                    catch (SerializationAbortException ex)
                    {
                        exitNode = false;
                        throw ex;
                    }
                    catch (Exception ex)
                    {
                        context.Config.DebugContext.LogException(ex);
                        return(default(T));
                    }
                    finally
                    {
                        if (exitNode)
                        {
                            reader.ExitNode();
                        }
                    }
                }

                //
                // The below cases are for when we expect an object, but have
                // serialized a straight primitive type. In such cases, we can
                // often box the primitive type as an object.
                //
                // Sadly, the exact primitive type might be lost in case of
                // integer and floating points numbers, as we don't know what
                // type to expect.
                //
                // To be safe, we read and box the most precise type available.
                //

                case EntryType.Boolean:
                {
                    if (!ComplexTypeMayBeBoxedValueType)
                    {
                        goto default;
                    }

                    bool value;
                    reader.ReadBoolean(out value);
                    return((T)(object)value);
                }

                case EntryType.FloatingPoint:
                {
                    if (!ComplexTypeMayBeBoxedValueType)
                    {
                        goto default;
                    }

                    double value;
                    reader.ReadDouble(out value);
                    return((T)(object)value);
                }

                case EntryType.Integer:
                {
                    if (!ComplexTypeMayBeBoxedValueType)
                    {
                        goto default;
                    }

                    long value;
                    reader.ReadInt64(out value);
                    return((T)(object)value);
                }

                case EntryType.String:
                {
                    if (!ComplexTypeMayBeBoxedValueType)
                    {
                        goto default;
                    }

                    string value;
                    reader.ReadString(out value);
                    return((T)(object)value);
                }

                case EntryType.Guid:
                {
                    if (!ComplexTypeMayBeBoxedValueType)
                    {
                        goto default;
                    }

                    Guid value;
                    reader.ReadGuid(out value);
                    return((T)(object)value);
                }

                default:

                    // Lost value somehow
                    context.Config.DebugContext.LogWarning("Unexpected entry of type " + entry.ToString() + ", when a reference or node start was expected. A value has been lost.");
                    reader.SkipEntry();
                    return(default(T));
                }
            }
        }
        /// <summary>
        /// Writes a value of type <see cref="T" />.
        /// </summary>
        /// <param name="name">The name of the value to write.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="writer">The writer to use.</param>
        public override void WriteValue(string name, T value, IDataWriter writer)
        {
            var context = writer.Context;
            var policy  = context.Config.SerializationPolicy;

            if (policy.AllowNonSerializableTypes == false && TypeOf_T.IsSerializable == false)
            {
                context.Config.DebugContext.LogError("The type " + TypeOf_T.Name + " is not marked as serializable.");
                return;
            }

            FireOnSerializedType();

            if (ComplexTypeIsValueType)
            {
                bool endNode = true;

                try
                {
                    writer.BeginStructNode(name, TypeOf_T);
                    GetBaseFormatter(policy).Serialize(value, writer);
                }
                catch (SerializationAbortException ex)
                {
                    endNode = false;
                    throw ex;
                }
                finally
                {
                    if (endNode)
                    {
                        writer.EndNode(name);
                    }
                }
            }
            else
            {
                int    id;
                int    index;
                string strId;
                Guid   guid;

                bool endNode = true;

                if (object.ReferenceEquals(value, null))
                {
                    writer.WriteNull(name);
                }
                else if (context.TryRegisterExternalReference(value, out index))
                {
                    writer.WriteExternalReference(name, index);
                }
                else if (context.TryRegisterExternalReference(value, out guid))
                {
                    writer.WriteExternalReference(name, guid);
                }
                else if (context.TryRegisterExternalReference(value, out strId))
                {
                    writer.WriteExternalReference(name, strId);
                }
                else if (context.TryRegisterInternalReference(value, out id))
                {
                    Type type = value.GetType(); // Get type of actual stored object

                    if (ComplexTypeMayBeBoxedValueType && FormatterUtilities.IsPrimitiveType(type))
                    // It's a boxed primitive type
                    {
                        try
                        {
                            writer.BeginReferenceNode(name, type, id);

                            var serializer = Serializer.Get(type);
                            serializer.WriteValueWeak(value, writer);
                        }
                        catch (SerializationAbortException ex)
                        {
                            endNode = false;
                            throw ex;
                        }
                        finally
                        {
                            if (endNode)
                            {
                                writer.EndNode(name);
                            }
                        }
                    }
                    else
                    {
                        IFormatter formatter;

                        if (object.ReferenceEquals(type, TypeOf_T))
                        {
                            formatter = GetBaseFormatter(policy);
                        }
                        else
                        {
                            formatter = FormatterLocator.GetFormatter(type, policy);
                        }

                        try
                        {
                            writer.BeginReferenceNode(name, type, id);
                            formatter.Serialize(value, writer);
                        }
                        catch (SerializationAbortException ex)
                        {
                            endNode = false;
                            throw ex;
                        }
                        finally
                        {
                            if (endNode)
                            {
                                writer.EndNode(name);
                            }
                        }
                    }
                }
                else
                {
                    writer.WriteInternalReference(name, id);
                }
            }
        }
Example #19
0
        private static IFormatter CreateFormatter(Type type, ISerializationPolicy policy)
        {
            if (FormatterUtilities.IsPrimitiveType(type))
            {
                throw new ArgumentException("Cannot create formatters for a primitive type like " + type.Name);
            }

            // First call formatter locators before checking for registered formatters
            for (int i = 0; i < FormatterLocators.Count; i++)
            {
                try
                {
                    IFormatter result;
                    if (FormatterLocators[i].LocatorInstance.TryGetFormatter(type, FormatterLocationStep.BeforeRegisteredFormatters, policy, out result))
                    {
                        return(result);
                    }
                }
                catch (TargetInvocationException ex)
                {
                    throw ex;
                }
                catch (TypeInitializationException ex)
                {
                    throw ex;
                }
#pragma warning disable CS0618 // Type or member is obsolete
                catch (ExecutionEngineException ex)
#pragma warning restore CS0618 // Type or member is obsolete
                {
                    throw ex;
                }
                catch (Exception ex)
                {
                    Debug.LogException(new Exception("Exception was thrown while calling FormatterLocator " + FormatterLocators[i].GetType().FullName + ".", ex));
                }
            }

            // Then check for valid registered formatters
            for (int i = 0; i < FormatterInfos.Count; i++)
            {
                var info = FormatterInfos[i];

                Type formatterType = null;

                if (type == info.TargetType)
                {
                    formatterType = info.FormatterType;
                }
                else if (info.FormatterType.IsGenericType && info.TargetType.IsGenericParameter)
                {
                    Type[] inferredArgs;

                    if (info.FormatterType.TryInferGenericParameters(out inferredArgs, type))
                    {
                        formatterType = info.FormatterType.GetGenericTypeDefinition().MakeGenericType(inferredArgs);
                    }
                }
                else if (type.IsGenericType && info.FormatterType.IsGenericType && info.TargetType.IsGenericType && type.GetGenericTypeDefinition() == info.TargetType.GetGenericTypeDefinition())
                {
                    Type[] args = type.GetGenericArguments();

                    if (info.FormatterType.AreGenericConstraintsSatisfiedBy(args))
                    {
                        formatterType = info.FormatterType.GetGenericTypeDefinition().MakeGenericType(args);
                    }
                }

                if (formatterType != null)
                {
                    var instance = GetFormatterInstance(formatterType);

                    if (instance == null)
                    {
                        continue;
                    }

                    if (info.AskIfCanFormatTypes && !((IAskIfCanFormatTypes)instance).CanFormatType(type))
                    {
                        continue;
                    }

                    return(instance);
                }
            }

            // Then call formatter locators after checking for registered formatters
            for (int i = 0; i < FormatterLocators.Count; i++)
            {
                try
                {
                    IFormatter result;
                    if (FormatterLocators[i].LocatorInstance.TryGetFormatter(type, FormatterLocationStep.AfterRegisteredFormatters, policy, out result))
                    {
                        return(result);
                    }
                }
                catch (TargetInvocationException ex)
                {
                    throw ex;
                }
                catch (TypeInitializationException ex)
                {
                    throw ex;
                }
#pragma warning disable CS0618 // Type or member is obsolete
                catch (ExecutionEngineException ex)
#pragma warning restore CS0618 // Type or member is obsolete
                {
                    throw ex;
                }
                catch (Exception ex)
                {
                    Debug.LogException(new Exception("Exception was thrown while calling FormatterLocator " + FormatterLocators[i].GetType().FullName + ".", ex));
                }
            }

            // If we can, emit a formatter to handle serialization of this object
            {
                if (EmitUtilities.CanEmit)
                {
                    var result = FormatterEmitter.GetEmittedFormatter(type, policy);
                    if (result != null)
                    {
                        return(result);
                    }
                }
            }

            if (EmitUtilities.CanEmit)
            {
                Debug.LogWarning("Fallback to reflection for type " + type.Name + " when emit is possible on this platform.");
            }

            // Finally, we fall back to a reflection-based formatter if nothing else has been found
            return((IFormatter)Activator.CreateInstance(typeof(ReflectionFormatter <>).MakeGenericType(type)));
        }
Example #20
0
        private static void ReplaceAllReferencesInGraph(object graph, object oldReference, object newReference, HashSet <object> processedReferences = null)
        {
            if (processedReferences == null)
            {
                processedReferences = new HashSet <object>(ReferenceEqualityComparer <object> .Default);
            }

            processedReferences.Add(graph);

            if (graph.GetType().IsArray)
            {
                Array array = (Array)graph;

                for (int i = 0; i < array.Length; i++)
                {
                    var value = array.GetValue(i);

                    if (object.ReferenceEquals(value, null))
                    {
                        continue;
                    }

                    if (object.ReferenceEquals(value, oldReference))
                    {
                        array.SetValue(newReference, i);
                        value = newReference;
                    }

                    if (!processedReferences.Contains(value))
                    {
                        ReplaceAllReferencesInGraph(value, oldReference, newReference, processedReferences);
                    }
                }
            }
            else
            {
                var members = FormatterUtilities.GetSerializableMembers(graph.GetType(), SerializationPolicies.Everything);

                for (int i = 0; i < members.Length; i++)
                {
                    FieldInfo field = (FieldInfo)members[i];

                    if (field.FieldType.IsPrimitive || field.FieldType == typeof(SerializationData) || field.FieldType == typeof(string))
                    {
                        continue;
                    }

                    object value = field.GetValue(graph);

                    if (object.ReferenceEquals(value, null))
                    {
                        continue;
                    }

                    Type valueType = value.GetType();

                    if (valueType.IsPrimitive || valueType == typeof(SerializationData) || valueType == typeof(string))
                    {
                        continue;
                    }

                    if (object.ReferenceEquals(value, oldReference))
                    {
                        field.SetValue(graph, newReference);
                        value = newReference;
                    }

                    if (!processedReferences.Contains(value))
                    {
                        ReplaceAllReferencesInGraph(value, oldReference, newReference, processedReferences);
                    }
                }
            }
        }