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); } }
/// <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); } } }
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))); }
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); }
/// <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 } }