ArrayTypeMismatchException when serializing nested Dictionary on Android device (after HybridCLR hot update)

#621 · closed · 2 comments

View on GitHub ↗

LiuZhanMingFpy

### JEngine Version 1.1.5 ### Unity Version 2022.3.62f2 ### Target Platform Android ### Bug Description Nino serialization throws `System.ArrayTypeMismatchException` when serializing a class containing **nested `Dictionary<int, CustomClass>`** structures. **Important**: This bug **only reproduces on Android real device**, NOT in Unity Editor or other platforms. The issue occurs under these conditions: 1. Running on **Android real device** (not Editor) 2. After entering **hot update scene** via HybridCLR 3. Nino generators have been properly initialized: - `HotUpdate.Code.NinoGen.Serializer.Init()` - `HotUpdate.Code.NinoGen.Deserializer.Init()` - `HotUpdate.Code.NinoGen.NinoBuiltInTypesRegistration.Init()` 4. Then dynamically create GameObject and add the component with nested Dictionary serialization Data structure: - Class `Root` has `Dictionary<int, Level1>` - Class `Level1` has `Dictionary<int, Level2>` - All classes are marked with `[NinoType]` ### Steps to Reproduce 1. Build and deploy to Android real device 2. Launch app and complete hot update process 3. Enter hot update scene 4. In EntryPoint method, initialize Nino: ```csharp HotUpdate.Code.NinoGen.Serializer.Init(); HotUpdate.Code.NinoGen.Deserializer.Init(); HotUpdate.Code.NinoGen.NinoBuiltInTypesRegistration.Init(); 5. Dynamically create test component: var a = new GameObject(); a.AddComponent<NinoNestedDictionaryRepro>(); ### Expected Behavior Serialization and deserialization should work correctly for nested `Dictionary<int, CustomClass>`structures, just like single-level dictionaries. ### Error Logs ```shell [NinoRepro] Exception: System.ArrayTypeMismatchException Attempted to access an element as a type incompatible with the array. at Nino.Core.CachedSerializer`1[T].SerializePolymorphic (T val, Nino.Core.Writer& writer) [0x00000] in <00000000000000000000000000000000>:0 at Nino.Core.CachedSerializer`1[T].Serialize (T val, Nino.Core.Writer& writer) [0x00000] in <00000000000000000000000000000000>:0 at Nino.Core.NinoSerializer.Serialize[T] (T value, Nino.Core.Writer& writer) [0x00000] in <00000000000000000000000000000000>:0 at Nino.Core.NinoSerializer.Serialize[T] (T value) [0x00000] in <00000000000000000000000000000000>:0 ``` ### Additional Context ``` using System; using System.Collections.Generic; using Nino.Core; using UnityEngine; public class NinoNestedDictionaryRepro : MonoBehaviour { [NinoType] public class Root { public Dictionary<int, Level1> RegionStates = new(); public float SavedRemainingTime; public int CurrentWorldMapId; } [NinoType] public class Level1 { public int RegionId; public Dictionary<int, Level2> ResourceStates = new(); } [NinoType] public class Level2 { public int ResourceId; public int RemainingCount; public int MaxCount; } private void Start() { try { var src = CreateSample(); byte[] bytes = NinoSerializer.Serialize(src); // ❌ Exception here Debug.Log($"[NinoRepro] Serialize OK, bytes={bytes.Length}"); var dst = NinoDeserializer.Deserialize<Root>(bytes); Debug.Log($"[NinoRepro] Deserialize OK, regions={dst.RegionStates.Count}"); } catch (Exception ex) { Debug.LogError($"[NinoRepro] Exception: {ex.GetType().FullName}\n{ex.Message}\n{ex.StackTrace}"); } } private static Root CreateSample() { var root = new Root { SavedRemainingTime = 12.34f, CurrentWorldMapId = 7 }; var region = new Level1 { RegionId = 1001 }; region.ResourceStates[2002] = new Level2 { ResourceId = 2002, RemainingCount = 3, MaxCount = 10 }; root.RegionStates[region.RegionId] = region; return root; } } ``` ### Checklist - [x] I have searched existing issues to ensure this bug hasn't been reported before - [x] I have tested with the latest version of JEngine

Comments

JasonXuDeveloper

Root cause identified: Nino's generated Dictionary serializer uses `Unsafe.As<Dictionary, DictionaryView>` to access the internal `_entries` array. HybridCLR's interpreter enforces strict type checking on `ldelema`, which detects that `DictionaryView.Entry` ≠ `Dictionary.Entry` and throws `ArrayTypeMismatchException`. This doesn't happen in the editor because Mono JIT treats `Unsafe.As` as an intrinsic with no runtime type check. Filed upstream: JasonXuDeveloper/Nino#172 This will be resolved by a Nino update once the upstream fix is available.

JasonXuDeveloper

Fixed by bumping Nino to 4.0.0-preview.147 in #627.