/// <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="M:OdinSerializer.BaseFormatter`1.GetUninitializedObject" />.</param> /// <param name="reader">The reader to deserialize with.</param> protected override void DeserializeImplementation(ref T value, IDataReader reader) { string name; EntryType entry; entry = reader.PeekEntry(out name); if (entry == EntryType.StartOfArray) { // We have legacy ISerializable data for the MethodInfo, since in no case will data written by this formatter ever start with an array. // In this case, get the proper legacy formatter for this type and read the data using that. IFormatter <T> serializableFormatter; try { serializableFormatter = (IFormatter <T>)Activator.CreateInstance(typeof(SerializableFormatter <>).MakeGenericType(typeof(T))); } catch (Exception) { reader.Context.Config.DebugContext.LogWarning("MethodInfo with legacy ISerializable data serialized was read in a context where a SerializableFormatter<T> formatter for the type could not be instantiated, likely in an IL2CPP build. This means legacy data cannot be read properly - please reserialize all data in your project to ensure no legacy MethodInfo data is included in your build, as this case is not AOT-supported by default."); value = null; return; } value = serializableFormatter.Deserialize(reader); return; } Type declaringType = null; string methodName = null; Type[] signature = null; Type[] genericArguments = null; while ((entry = reader.PeekEntry(out name)) != EntryType.EndOfNode && entry != EntryType.EndOfArray && entry != EntryType.EndOfStream) { switch (name) { case "declaringType": { var t = TypeSerializer.ReadValue(reader); if (t != null) { declaringType = t; } } break; case "methodName": { methodName = StringSerializer.ReadValue(reader); } break; case "signature": { signature = TypeArraySerializer.ReadValue(reader); } break; case "genericArguments": { genericArguments = TypeArraySerializer.ReadValue(reader); } break; default: reader.SkipEntry(); break; } } if (declaringType == null) { reader.Context.Config.DebugContext.LogWarning("Missing declaring type of MethodInfo on deserialize."); return; } if (methodName == null) { reader.Context.Config.DebugContext.LogError("Missing method name of MethodInfo on deserialize."); return; } MethodInfo methodInfo; bool useSignature = false; bool wasAmbiguous = false; if (signature != null) { useSignature = true; for (int i = 0; i < signature.Length; i++) { if (signature[i] == null) { useSignature = false; break; } } } if (useSignature) { try { methodInfo = declaringType.GetMethod(methodName, Flags.AllMembers, null, signature, null); } catch (AmbiguousMatchException) { methodInfo = null; wasAmbiguous = true; } } else { try { methodInfo = declaringType.GetMethod(methodName, Flags.AllMembers); } catch (AmbiguousMatchException) { methodInfo = null; wasAmbiguous = true; } } if (methodInfo == null) { if (useSignature) { reader.Context.Config.DebugContext.LogWarning("Could not find method with signature " + name + "(" + string.Join(", ", signature.Select(p => p.GetNiceFullName()).ToArray()) + ") on type '" + declaringType.FullName + (wasAmbiguous ? "; resolution was ambiguous between multiple methods" : string.Empty) + "."); } else { reader.Context.Config.DebugContext.LogWarning("Could not find method with name " + name + " on type '" + declaringType.GetNiceFullName() + (wasAmbiguous ? "; resolution was ambiguous between multiple methods" : string.Empty) + "."); } return; } if (methodInfo.IsGenericMethodDefinition) { if (genericArguments == null) { reader.Context.Config.DebugContext.LogWarning("Method '" + declaringType.GetNiceFullName() + "." + methodInfo.GetNiceName() + "' to deserialize is a generic method definition, but no generic arguments were in the serialization data."); return; } int argCount = methodInfo.GetGenericArguments().Length; if (genericArguments.Length != argCount) { reader.Context.Config.DebugContext.LogWarning("Method '" + declaringType.GetNiceFullName() + "." + methodInfo.GetNiceName() + "' to deserialize is a generic method definition, but there is the wrong number of generic arguments in the serialization data."); return; } for (int i = 0; i < genericArguments.Length; i++) { if (genericArguments[i] == null) { reader.Context.Config.DebugContext.LogWarning("Method '" + declaringType.GetNiceFullName() + "." + methodInfo.GetNiceName() + "' to deserialize is a generic method definition, but one of the serialized generic argument types failed to bind on deserialization."); return; } } try { methodInfo = methodInfo.MakeGenericMethod(genericArguments); } catch (Exception ex) { reader.Context.Config.DebugContext.LogWarning("Method '" + declaringType.GetNiceFullName() + "." + methodInfo.GetNiceName() + "' to deserialize is a generic method definition, but failed to create generic method from definition, using generic arguments '" + string.Join(", ", genericArguments.Select(p => p.GetNiceFullName()).ToArray()) + "'. Method creation failed with an exception of type " + ex.GetType().GetNiceFullName() + ", with the message: " + ex.Message); return; } } try { value = (T)methodInfo; } catch (InvalidCastException) { reader.Context.Config.DebugContext.LogWarning("The serialized method '" + declaringType.GetNiceFullName() + "." + methodInfo.GetNiceName() + "' was successfully resolved into a MethodInfo reference of the runtime type '" + methodInfo.GetType().GetNiceFullName() + "', but failed to be cast to expected type '" + typeof(T).GetNiceFullName() + "'."); return; } this.RegisterReferenceID(value, reader); }
/// <summary> /// Reads into the specified value using the specified reader. /// </summary> /// <param name="value">The value to read into.</param> /// <param name="reader">The reader to use.</param> protected override void Read(ref Bounds value, IDataReader reader) { value.center = Vector3Serializer.ReadValue(reader); value.size = Vector3Serializer.ReadValue(reader); }
/// <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="M:OdinSerializer.BaseFormatter`1.GetUninitializedObject" />.</param> /// <param name="reader">The reader to deserialize with.</param> protected override void DeserializeImplementation(ref T value, IDataReader reader) { string name; EntryType entry; Type delegateType = typeof(T); Type declaringType = null; object target = null; string methodName = null; Type[] signature = null; Type[] genericArguments = null; Delegate[] invocationList = null; while ((entry = reader.PeekEntry(out name)) != EntryType.EndOfNode && entry != EntryType.EndOfArray && entry != EntryType.EndOfStream) { switch (name) { case "invocationList": { invocationList = DelegateArraySerializer.ReadValue(reader); } break; case "target": { target = ObjectSerializer.ReadValue(reader); } break; case "declaringType": { var t = TypeSerializer.ReadValue(reader); if (t != null) { declaringType = t; } } break; case "methodName": { methodName = StringSerializer.ReadValue(reader); } break; case "delegateType": { var t = TypeSerializer.ReadValue(reader); if (t != null) { delegateType = t; } } break; case "signature": { signature = TypeArraySerializer.ReadValue(reader); } break; case "genericArguments": { genericArguments = TypeArraySerializer.ReadValue(reader); } break; default: reader.SkipEntry(); break; } } if (invocationList != null) { Delegate combinedDelegate = null; try { combinedDelegate = Delegate.Combine(invocationList); } catch (Exception ex) { reader.Context.Config.DebugContext.LogError("Recombining delegate invocation list upon deserialization failed with an exception of type " + ex.GetType().GetNiceFullName() + " with the message: " + ex.Message); } if (combinedDelegate != null) { try { value = (T)(object)combinedDelegate; } catch (InvalidCastException) { reader.Context.Config.DebugContext.LogWarning("Could not cast recombined delegate of type " + combinedDelegate.GetType().GetNiceFullName() + " to expected delegate type " + typeof(T).GetNiceFullName() + "."); } } return; } if (declaringType == null) { reader.Context.Config.DebugContext.LogWarning("Missing declaring type of delegate on deserialize."); return; } if (methodName == null) { reader.Context.Config.DebugContext.LogError("Missing method name of delegate on deserialize."); return; } MethodInfo methodInfo; bool useSignature = false; bool wasAmbiguous = false; if (signature != null) { useSignature = true; for (int i = 0; i < signature.Length; i++) { if (signature[i] == null) { useSignature = false; break; } } } if (useSignature) { try { methodInfo = declaringType.GetMethod(methodName, Flags.AllMembers, null, signature, null); } catch (AmbiguousMatchException) { methodInfo = null; wasAmbiguous = true; } } else { try { methodInfo = declaringType.GetMethod(methodName, Flags.AllMembers); } catch (AmbiguousMatchException) { methodInfo = null; wasAmbiguous = true; } } if (methodInfo == null) { if (useSignature) { reader.Context.Config.DebugContext.LogWarning("Could not find method with signature " + name + "(" + string.Join(", ", signature.Select(p => p.GetNiceFullName()).ToArray()) + ") on type '" + declaringType.FullName + (wasAmbiguous ? "; resolution was ambiguous between multiple methods" : string.Empty) + "."); } else { reader.Context.Config.DebugContext.LogWarning("Could not find method with name " + name + " on type '" + declaringType.GetNiceFullName() + (wasAmbiguous ? "; resolution was ambiguous between multiple methods" : string.Empty) + "."); } return; } if (methodInfo.IsGenericMethodDefinition) { if (genericArguments == null) { reader.Context.Config.DebugContext.LogWarning("Method '" + declaringType.GetNiceFullName() + "." + methodInfo.GetNiceName() + "' of delegate to deserialize is a generic method definition, but no generic arguments were in the serialization data."); return; } int argCount = methodInfo.GetGenericArguments().Length; if (genericArguments.Length != argCount) { reader.Context.Config.DebugContext.LogWarning("Method '" + declaringType.GetNiceFullName() + "." + methodInfo.GetNiceName() + "' of delegate to deserialize is a generic method definition, but there is the wrong number of generic arguments in the serialization data."); return; } for (int i = 0; i < genericArguments.Length; i++) { if (genericArguments[i] == null) { reader.Context.Config.DebugContext.LogWarning("Method '" + declaringType.GetNiceFullName() + "." + methodInfo.GetNiceName() + "' of delegate to deserialize is a generic method definition, but one of the serialized generic argument types failed to bind on deserialization."); return; } } try { methodInfo = methodInfo.MakeGenericMethod(genericArguments); } catch (Exception ex) { reader.Context.Config.DebugContext.LogWarning("Method '" + declaringType.GetNiceFullName() + "." + methodInfo.GetNiceName() + "' of delegate to deserialize is a generic method definition, but failed to create generic method from definition, using generic arguments '" + string.Join(", ", genericArguments.Select(p => p.GetNiceFullName()).ToArray()) + "'. Method creation failed with an exception of type " + ex.GetType().GetNiceFullName() + ", with the message: " + ex.Message); return; } } if (methodInfo.IsStatic) { value = (T)(object)Delegate.CreateDelegate(delegateType, methodInfo, false); } else { Type targetType = methodInfo.DeclaringType; if (typeof(UnityEngine.Object).IsAssignableFrom(targetType)) { if ((target as UnityEngine.Object) == null) { reader.Context.Config.DebugContext.LogWarning("Method '" + declaringType.GetNiceFullName() + "." + methodInfo.GetNiceName() + "' of delegate to deserialize is an instance method, but Unity object target of type '" + targetType.GetNiceFullName() + "' was null on deserialization. Did something destroy it, or did you apply a delegate value targeting a scene-based UnityEngine.Object instance to a prefab?"); return; } } else { if (object.ReferenceEquals(target, null)) { reader.Context.Config.DebugContext.LogWarning("Method '" + declaringType.GetNiceFullName() + "." + methodInfo.GetNiceName() + "' of delegate to deserialize is an instance method, but no valid instance target of type '" + targetType.GetNiceFullName() + "' was in the serialization data. Has something been renamed since serialization?"); return; } } value = (T)(object)Delegate.CreateDelegate(delegateType, target, methodInfo, false); } if (value == null) { reader.Context.Config.DebugContext.LogWarning("Failed to create delegate of type " + delegateType.GetNiceFullName() + " from method '" + declaringType.GetNiceFullName() + "." + methodInfo.GetNiceName() + "'."); return; } this.RegisterReferenceID(value, reader); this.InvokeOnDeserializingCallbacks(ref value, reader.Context); }
/// <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 TDictionary value, IDataReader reader) { string name; var entry = reader.PeekEntry(out name); IEqualityComparer <TKey> comparer = null; if (name == "comparer" || entry == EntryType.StartOfNode) { // There is a comparer serialized comparer = EqualityComparerSerializer.ReadValue(reader); entry = reader.PeekEntry(out name); } if (entry == EntryType.StartOfArray) { try { long length; reader.EnterArray(out length); Type type; if (!object.ReferenceEquals(comparer, null) && ComparerConstructor != null) { value = (TDictionary)ComparerConstructor.Invoke(new object[] { comparer }); } else { value = new TDictionary(); } // We must remember to register the dictionary reference ourselves, since we returned null in GetUninitializedObject this.RegisterReferenceID(value, reader); // There aren't any OnDeserializing callbacks on dictionaries that we're interested in. // Hence we don't invoke this.InvokeOnDeserializingCallbacks(value, reader, context); for (int i = 0; i < length; i++) { if (reader.PeekEntry(out name) == EntryType.EndOfArray) { reader.Context.Config.DebugContext.LogError("Reached end of array after " + i + " elements, when " + length + " elements were expected."); break; } bool exitNode = true; try { reader.EnterNode(out type); TKey key = KeyReaderWriter.ReadValue(reader); TValue val = ValueReaderWriter.ReadValue(reader); if (!KeyIsValueType && object.ReferenceEquals(key, null)) { reader.Context.Config.DebugContext.LogWarning("Dictionary key of type '" + typeof(TKey).FullName + "' was null upon deserialization. A key has gone missing."); continue; } value[key] = val; } catch (SerializationAbortException ex) { exitNode = false; throw ex; } catch (Exception ex) { reader.Context.Config.DebugContext.LogException(ex); } finally { if (exitNode) { reader.ExitNode(); } } if (reader.IsInArrayNode == false) { reader.Context.Config.DebugContext.LogError("Reading array went wrong. Data dump: " + reader.GetDataDump()); break; } } } finally { reader.ExitArray(); } } else { reader.SkipEntry(); } }
/// <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 TArray value, IDataReader reader) { string name; var entry = reader.PeekEntry(out name); if (entry == EntryType.StartOfArray) { long length; reader.EnterArray(out length); entry = reader.PeekEntry(out name); if (entry != EntryType.String || name != RANKS_NAME) { value = default(TArray); reader.SkipEntry(); return; } string lengthStr; reader.ReadString(out lengthStr); string[] lengthsStrs = lengthStr.Split(RANKS_SEPARATOR); if (lengthsStrs.Length != ArrayRank) { value = default(TArray); reader.SkipEntry(); return; } int[] lengths = new int[lengthsStrs.Length]; for (int i = 0; i < lengthsStrs.Length; i++) { int rankVal; if (int.TryParse(lengthsStrs[i], out rankVal)) { lengths[i] = rankVal; } else { value = default(TArray); reader.SkipEntry(); return; } } long rankTotal = lengths[0]; for (int i = 1; i < lengths.Length; i++) { rankTotal *= lengths[i]; } if (rankTotal != length) { value = default(TArray); reader.SkipEntry(); return; } value = (TArray)(object)Array.CreateInstance(typeof(TElement), lengths); // We must remember to register the array reference ourselves, since we return null in GetUninitializedObject this.RegisterReferenceID(value, reader); // There aren't any OnDeserializing callbacks on arrays. // Hence we don't invoke this.InvokeOnDeserializingCallbacks(value, reader, context); int elements = 0; try { this.IterateArrayWrite( (Array)(object)value, () => { if (reader.PeekEntry(out name) == EntryType.EndOfArray) { reader.Context.Config.DebugContext.LogError("Reached end of array after " + elements + " elements, when " + length + " elements were expected."); throw new InvalidOperationException(); } var v = ValueReaderWriter.ReadValue(reader); if (reader.IsInArrayNode == false) { reader.Context.Config.DebugContext.LogError("Reading array went wrong. Data dump: " + reader.GetDataDump()); throw new InvalidOperationException(); } elements++; return(v); }); } catch (InvalidOperationException) { } catch (Exception ex) { reader.Context.Config.DebugContext.LogException(ex); } reader.ExitArray(); } else { value = default(TArray); reader.SkipEntry(); } }