Remove LINQ calls from serialization system for better AOT compatibility

Issue #401 resolved
Tor Esa Vestergaard created an issue

On some versions of Unity, when compiling AOT targeting some specific platforms (only confirmed for PS4 on Unity 2017.4), LINQ calls in Odin's serialization system will not be AOT-compiled properly, leading to invalid JIT invocations on the AOT platform at runtime.

The following exceptions at runtime are reported when targeting PS4 on Unity 2017.3:

#!

ExecutionEngineException: Attempting to JIT compile method 'System.Linq.OrderedEnumerable`1<Sirenix.Serialization.FormatterLocator/FormatterInfo>:CreateOrderedEnumerable<string> (System.Func`2<Sirenix.Serialization.FormatterLocator/FormatterInfo, string>,System.Collections.Generic.IComparer`1<string>,bool)' while running with --aot-only.

  at System.Linq.Enumerable.ThenByDescending[FormatterInfo,String] (IOrderedEnumerable`1 source, System.Func`2 keySelector, IComparer`1 comparer) [0x00000] in <filename unknown>:0 
  at System.Linq.Enumerable.ThenByDescending[FormatterInfo,String] (IOrderedEnumerable`1 source, System.Func`2 keySelector) [0x00000] in <filename unknown>:0 
  at Sirenix.Serialization.FormatterLocator..cctor () [0x00000] in <filename unknown>:0 
Rethrow as TypeInitializationException: An exception was thrown by the type initializer for Sirenix.Serialization.FormatterLocator
  at Sirenix.Serialization.ComplexTypeSerializer`1[System.Collections.Generic.List`1[EpsilonGames.Gameplay.ProgressionContainer]].ReadValue (IDataReader reader) [0x00000] in <filename unknown>:0 
UnityEngine.DebugLogHandler:Internal_LogException(Exception, Object)
UnityEngine.DebugLogHandler:LogException(Exception, Object)
UnityEngine.Logger:LogException(Exception, Object)
UnityEngine.Debug:LogException(Exception)
Sirenix.Serialization.CustomLogger:LogException(Exception)
Sirenix.Serialization.DebugContext:LogException(Exception)
Sirenix.Serialization.UnitySerializationUtility:DeserializeUnityObject(Object, IDataReader)
Sirenix.Serialization.UnitySerializationUtility:DeserializeUnityObject(Object, Byte[]&, List`1&, DataFormat, DeserializationContext)
Sirenix.Serialization.UnitySerializationUtility:DeserializeUnityObject(Object, SerializationData&, DeserializationContext, Boolean, List`1)
Sirenix.Serialization.UnitySerializationUtility:DeserializeUnityObject(Object, SerializationData&, DeserializationContext)
Sirenix.OdinInspector.SerializedScriptableObject:UnityEngine.ISerializationCallbackReceiver.OnAfterDeserialize()
#!

NullReferenceException: A null value was found where an object instance was required.
  at Sirenix.Serialization.Utilities.Cache`1[Sirenix.Serialization.DeserializationContext].Claim () [0x00000] in <filename unknown>:0 
  at Sirenix.Serialization.UnitySerializationUtility.DeserializeUnityObject (UnityEngine.Object unityObject, Sirenix.Serialization.SerializationData& data, Sirenix.Serialization.DeserializationContext context, Boolean isPrefabData, System.Collections.Generic.List`1 prefabInstanceUnityObjects) [0x00000] in <filename unknown>:0 
  at Sirenix.Serialization.UnitySerializationUtility.DeserializeUnityObject (UnityEngine.Object unityObject, Sirenix.Serialization.SerializationData& data, Sirenix.Serialization.DeserializationContext context) [0x00000] in <filename unknown>:0 
  at Sirenix.OdinInspector.SerializedMonoBehaviour.UnityEngine.ISerializationCallbackReceiver.OnAfterDeserialize () [0x00000] in <filename unknown>:0 

While this fix is likely to complicate the code somewhat, it also has the benefit of being likely to improve the serialization initialization time marginally.

Comments (1)

  1. Log in to comment