Skip to content

ArrayTypeMismatchException when serializing Dictionary fields in HybridCLR #172

Description

@JasonXuDeveloper

Environment

  • Nino: 4.0.0-preview.143
  • HybridCLR: 8.9.0
  • Unity: 2022.3

Description

Serializing any [NinoType] class that contains a Dictionary<K,V> field throws System.ArrayTypeMismatchException when running in HybridCLR (hot-update interpreter). Works fine in the Unity editor (Mono JIT).

Minimal Reproduction

[NinoType]
public class Repro
{
    public Dictionary<int, int> Data = new() { { 1, 100 } };
}

// In MonoBehaviour.Start():
var bytes = NinoSerializer.Serialize(new Repro()); // ❌ ArrayTypeMismatchException

Nested dictionaries are also affected:

[NinoType]
public class Root
{
    public Dictionary<int, Child> Children = new();
}

[NinoType]
public class Child
{
    public int Id;
    public Dictionary<int, int> Values = new();
}

Stack Trace

System.ArrayTypeMismatchException: Attempted to access an element as a type incompatible with the array.
  at NinoGen.Serializer.Serialize (Dictionary`2[TKey,TValue] value, Writer& writer)
  at NinoGen.Serializer.SerializeImpl (Root value, Writer& writer)
  at Nino.Core.NinoSerializer.Serialize[T] (T value, Writer& writer)
  at Nino.Core.NinoSerializer.Serialize[T] (T value)

Root Cause

The generated Serialize(Dictionary<K,V>, ref Writer) method uses:

Entry<K,V>[] entries = Unsafe.As<Dictionary<K,V>, DictionaryView<K,V>>(ref value)._entries;
ref Entry<K,V> source = ref entries[0]; // ❌ throws here

Unsafe.As reinterprets the Dictionary reference as a DictionaryView to access the internal _entries field. The runtime array type is Dictionary<K,V>.Entry[] but the code accesses it as DictionaryView<K,V>.Entry[].

  • Editor (Mono JIT): Unsafe.As is a JIT intrinsic, no runtime type checking on array element access → works
  • HybridCLR (interpreter): The ldelema IL instruction performs runtime type checking, detects DictionaryView.EntryDictionary.EntryArrayTypeMismatchException

Suggested Fix

The generated Dictionary serializer needs a HybridCLR-compatible code path, for example using Dictionary.GetEnumerator() instead of Unsafe.As<Dictionary, DictionaryView> when targeting interpreters.


Ref: JasonXuDeveloper/JEngine#621

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions