From 20710235486e7ba9cba5642f8c4c590af7bfbf9c Mon Sep 17 00:00:00 2001 From: George Gromov Date: Thu, 29 Feb 2024 12:49:16 +0300 Subject: [PATCH 1/9] fixed importing UnityConverters (included issue #75) --- .../Utility/TypeCache.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs b/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs index bef86ae..40a4526 100644 --- a/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs +++ b/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs @@ -16,8 +16,16 @@ private static readonly Assembly[] _assemblies // When searching for types we want to look in mscorlib last // and Newtonsoft.Json up as the first ones .Reverse() + .OrderBy(Order) .ToArray(); + private static int Order(Assembly assembly) + { + if (assembly.FullName.StartsWith("Newtonsoft.Json")) return -1; + if (assembly.FullName == "mscorlib") return 1; + return 0; + } + public static Type FindType(string name) { if (_typeByName.TryGetValue(name, out var type)) From a748e5f1abe9741258d1b354bc454ab95a4580e3 Mon Sep 17 00:00:00 2001 From: George Gromov Date: Thu, 29 Feb 2024 17:02:45 +0300 Subject: [PATCH 2/9] fixed handling mscorlib --- .../Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs b/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs index 40a4526..8367c4b 100644 --- a/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs +++ b/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs @@ -22,7 +22,7 @@ private static readonly Assembly[] _assemblies private static int Order(Assembly assembly) { if (assembly.FullName.StartsWith("Newtonsoft.Json")) return -1; - if (assembly.FullName == "mscorlib") return 1; + if (assembly.FullName.StartsWith("mscorlib")) return 1; return 0; } From ddb3fd3180b25b4afe3ea0afb47db46c6da93cff Mon Sep 17 00:00:00 2001 From: Applejag Date: Thu, 29 Feb 2024 18:29:45 +0100 Subject: [PATCH 3/9] Changed to store AssemblyName in ConverterConfig for better type lookups --- ...Newtonsoft.Json-for-Unity.Converters.asset | 46 +++++++ .../Configuration/ConverterConfig.cs | 8 +- .../Editor/UnityConvertersConfigEditor.cs | 29 ++++- .../UnityConverterInitializer.cs | 12 +- .../Utility/TypeCache.cs | 117 ++++++++++++++---- 5 files changed, 178 insertions(+), 34 deletions(-) diff --git a/Assets/Resources/Newtonsoft.Json-for-Unity.Converters.asset b/Assets/Resources/Newtonsoft.Json-for-Unity.Converters.asset index 1266bae..b06496c 100644 --- a/Assets/Resources/Newtonsoft.Json-for-Unity.Converters.asset +++ b/Assets/Resources/Newtonsoft.Json-for-Unity.Converters.asset @@ -19,142 +19,188 @@ MonoBehaviour: unityConverters: - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Addressables.AssetReferenceConverter + converterAssembly: Newtonsoft.Json.UnityConverters.Addressables settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.AI.NavMesh.NavMeshQueryFilterConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.AI.NavMesh.NavMeshTriangulationConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Camera.CullingGroupEventConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Geometry.BoundsConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Geometry.BoundsIntConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Geometry.PlaneConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Geometry.RectConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Geometry.RectIntConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Geometry.RectOffsetConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Graphics.ResolutionConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Hashing.Hash128Converter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Math.Color32Converter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Math.ColorConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Math.Matrix4x4Converter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Math.QuaternionConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Math.SphericalHarmonicsL2Converter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Math.Vector2Converter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Math.Vector2IntConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Math.Vector3Converter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Math.Vector3IntConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Math.Vector4Converter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.NativeArray.NativeArrayConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Physics.JointDriveConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Physics.JointLimitsConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Physics.SoftJointLimitConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Physics2D.ColliderDistance2DConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Physics2D.ContactFilter2DConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Random.RandomStateConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Scripting.LayerMaskConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] - enabled: 1 converterName: Newtonsoft.Json.UnityConverters.Scripting.RangeIntConverter + converterAssembly: Newtonsoft.Json.UnityConverters settings: [] useAllJsonNetConverters: 0 jsonNetConverters: - enabled: 1 converterName: Newtonsoft.Json.Converters.StringEnumConverter + converterAssembly: Newtonsoft.Json settings: [] - enabled: 1 converterName: Newtonsoft.Json.Converters.VersionConverter + converterAssembly: Newtonsoft.Json settings: [] - enabled: 0 converterName: Newtonsoft.Json.Converters.BinaryConverter + converterAssembly: Newtonsoft.Json settings: [] - enabled: 0 converterName: Newtonsoft.Json.Converters.BsonObjectIdConverter + converterAssembly: Newtonsoft.Json settings: [] - enabled: 0 converterName: Newtonsoft.Json.Converters.DataSetConverter + converterAssembly: Newtonsoft.Json settings: [] - enabled: 0 converterName: Newtonsoft.Json.Converters.DataTableConverter + converterAssembly: Newtonsoft.Json settings: [] - enabled: 0 converterName: Newtonsoft.Json.Converters.DiscriminatedUnionConverter + converterAssembly: Newtonsoft.Json settings: [] - enabled: 0 converterName: Newtonsoft.Json.Converters.EntityKeyMemberConverter + converterAssembly: Newtonsoft.Json settings: [] - enabled: 0 converterName: Newtonsoft.Json.Converters.ExpandoObjectConverter + converterAssembly: Newtonsoft.Json settings: [] - enabled: 0 converterName: Newtonsoft.Json.Converters.IsoDateTimeConverter + converterAssembly: Newtonsoft.Json settings: [] - enabled: 0 converterName: Newtonsoft.Json.Converters.JavaScriptDateTimeConverter + converterAssembly: Newtonsoft.Json settings: [] - enabled: 0 converterName: Newtonsoft.Json.Converters.KeyValuePairConverter + converterAssembly: Newtonsoft.Json settings: [] - enabled: 0 converterName: Newtonsoft.Json.Converters.RegexConverter + converterAssembly: Newtonsoft.Json settings: [] - enabled: 0 converterName: Newtonsoft.Json.Converters.UnixDateTimeConverter + converterAssembly: Newtonsoft.Json settings: [] - enabled: 0 converterName: Newtonsoft.Json.Converters.XmlNodeConverter + converterAssembly: Newtonsoft.Json settings: [] autoSyncConverters: 1 diff --git a/Packages/Newtonsoft.Json-for-Unity.Converters/Configuration/ConverterConfig.cs b/Packages/Newtonsoft.Json-for-Unity.Converters/Configuration/ConverterConfig.cs index f1551e9..fda5161 100644 --- a/Packages/Newtonsoft.Json-for-Unity.Converters/Configuration/ConverterConfig.cs +++ b/Packages/Newtonsoft.Json-for-Unity.Converters/Configuration/ConverterConfig.cs @@ -12,11 +12,13 @@ public struct ConverterConfig : IEquatable public string converterName; + public string converterAssembly; + public List settings; public override string ToString() { - return $"{{enabled={enabled}, converterName={converterName}, settings=[{settings?.Count ?? 0}]}}"; + return $"{{enabled={enabled}, converterName={converterName}, assembly={converterAssembly}, settings=[{settings?.Count ?? 0}]}}"; } public override bool Equals(object obj) @@ -28,6 +30,7 @@ public bool Equals(ConverterConfig other) { return enabled == other.enabled && converterName == other.converterName && + converterAssembly == other.converterAssembly && EqualityComparer>.Default.Equals(settings, other.settings); } @@ -35,9 +38,10 @@ public bool Equals(ConverterConfig other) public override int GetHashCode() #pragma warning restore S2328 // "GetHashCode" should not reference mutable fields { - int hashCode = 1016066258; + int hashCode = 913629501; hashCode = hashCode * -1521134295 + enabled.GetHashCode(); hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(converterName); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(converterAssembly); hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(settings); return hashCode; } diff --git a/Packages/Newtonsoft.Json-for-Unity.Converters/Editor/UnityConvertersConfigEditor.cs b/Packages/Newtonsoft.Json-for-Unity.Converters/Editor/UnityConvertersConfigEditor.cs index 1e6e7ce..8c293fd 100644 --- a/Packages/Newtonsoft.Json-for-Unity.Converters/Editor/UnityConvertersConfigEditor.cs +++ b/Packages/Newtonsoft.Json-for-Unity.Converters/Editor/UnityConvertersConfigEditor.cs @@ -150,11 +150,27 @@ public override void OnInspectorGUI() private void AddAndSetupConverters(SerializedProperty arrayProperty, IList converterTypes, bool newAreEnabledByDefault) { - var elements = EnumerateArrayElements(arrayProperty); + var elements = EnumerateArrayElements(arrayProperty).ToArray(); var elementTypes = elements - .Select(e => TypeCache.FindType(e.FindPropertyRelative(nameof(ConverterConfig.converterName)).stringValue)) + .Select(e => TypeCache.FindType( + name: e.FindPropertyRelative(nameof(ConverterConfig.converterName)).stringValue, + assemblyName: e.FindPropertyRelative(nameof(ConverterConfig.converterAssembly)).stringValue + )) .ToArray(); + // Refresh missing fields on existing types + for (int i = 0; i < elements.Length; i++) + { + SerializedProperty elem = elements[i]; + Type type = elementTypes[i]; + + var assemblyNameProp = elem.FindPropertyRelative(nameof(ConverterConfig.converterAssembly)); + if (string.IsNullOrEmpty(assemblyNameProp.stringValue)) + { + assemblyNameProp.stringValue = type.Assembly.GetName().Name; + } + } + Type[] missingConverters = converterTypes .Where(type => !elementTypes.Contains(type)) .ToArray(); @@ -166,7 +182,7 @@ private void AddAndSetupConverters(SerializedProperty arrayProperty, IList { continue; } - var typeName = arrayProperty.GetArrayElementAtIndex(i).FindPropertyRelative(nameof(ConverterConfig.converterName)).stringValue; + string typeName = arrayProperty.GetArrayElementAtIndex(i).FindPropertyRelative(nameof(ConverterConfig.converterName)).stringValue; Debug.Log($"Removed type from JsonConverter list: \"{typeName}\"", target); arrayProperty.DeleteArrayElementAtIndex(i); } @@ -179,9 +195,11 @@ private void AddAndSetupConverters(SerializedProperty arrayProperty, IList SerializedProperty elemProp = arrayProperty.GetArrayElementAtIndex(nextIndex); SerializedProperty enabledProp = elemProp.FindPropertyRelative(nameof(ConverterConfig.enabled)); SerializedProperty converterNameProp = elemProp.FindPropertyRelative(nameof(ConverterConfig.converterName)); + SerializedProperty assemblyNameProp = elemProp.FindPropertyRelative(nameof(ConverterConfig.converterAssembly)); enabledProp.boolValue = newAreEnabledByDefault; converterNameProp.stringValue = converterType.FullName; + assemblyNameProp.stringValue = converterType.Assembly.GetName().Name; } } @@ -236,7 +254,10 @@ private void FoldoutConvertersList(SerializedProperty property, AnimBool fadedAn var allConfigsWithType = EnumerateArrayElements(property) .Select(o => ( serializedProperty: o, - type: TypeCache.FindType(o.FindPropertyRelative(nameof(ConverterConfig.converterName)).stringValue) + type: TypeCache.FindType( + name: o.FindPropertyRelative(nameof(ConverterConfig.converterName)).stringValue, + assemblyName: o.FindPropertyRelative(nameof(ConverterConfig.converterAssembly)).stringValue + ) )) .Where(o => o.type != null) .OrderBy(o => o.type.FullName); diff --git a/Packages/Newtonsoft.Json-for-Unity.Converters/UnityConverters/UnityConverterInitializer.cs b/Packages/Newtonsoft.Json-for-Unity.Converters/UnityConverters/UnityConverterInitializer.cs index 901d605..e73ddcf 100644 --- a/Packages/Newtonsoft.Json-for-Unity.Converters/UnityConverters/UnityConverterInitializer.cs +++ b/Packages/Newtonsoft.Json-for-Unity.Converters/UnityConverters/UnityConverterInitializer.cs @@ -157,15 +157,15 @@ internal static ConverterGrouping FindGroupedConverters(UnityConvertersConfig co } return new ConverterGrouping { - outsideConverters = config.outsideConverters.Select(x => GetTypeOrLog(x.converterName)).WhereNotNullRef().ToList(), - unityConverters = config.unityConverters.Select(x => GetTypeOrLog(x.converterName)).WhereNotNullRef().ToList(), - jsonNetConverters = config.jsonNetConverters.Select(x => GetTypeOrLog(x.converterName)).WhereNotNullRef().ToList(), + outsideConverters = config.outsideConverters.Select(x => GetTypeOrLog(x.converterName, x.converterAssembly)).WhereNotNullRef().ToList(), + unityConverters = config.unityConverters.Select(x => GetTypeOrLog(x.converterName, x.converterAssembly)).WhereNotNullRef().ToList(), + jsonNetConverters = config.jsonNetConverters.Select(x => GetTypeOrLog(x.converterName, x.converterAssembly)).WhereNotNullRef().ToList(), }; } - private static Type GetTypeOrLog(string name) + private static Type GetTypeOrLog(string name, string assemblyName) { - var type = TypeCache.FindType(name); + var type = TypeCache.FindType(name, assemblyName); if (type == null) { Debug.LogWarning($"Failed to lookup JsonConverter type. Ignoring it. Type name: \"{name}\""+ @@ -215,7 +215,7 @@ private static IEnumerable ApplyConfigFilter(IEnumerable types, bool var typesOfEnabledThroughConfig = configs .Where(o => o.enabled) - .Select(o => Utility.TypeCache.FindType(o.converterName)) + .Select(o => Utility.TypeCache.FindType(o.converterName, o.converterAssembly)) .Where(o => o != null); var hashMap = new HashSet(typesOfEnabledThroughConfig); diff --git a/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs b/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs index 8367c4b..21e8ffe 100644 --- a/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs +++ b/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs @@ -9,51 +9,124 @@ internal static class TypeCache { private static readonly Dictionary _typeByName = new Dictionary(); + private static readonly Dictionary, Type> _typeByNameAndAssembly + = new Dictionary, Type>(); + private static readonly Dictionary _assemblyByName + = new Dictionary(); - private static readonly Assembly[] _assemblies - = AppDomain.CurrentDomain.GetAssemblies() - // Reversing so we get last imported assembly first. - // When searching for types we want to look in mscorlib last - // and Newtonsoft.Json up as the first ones - .Reverse() - .OrderBy(Order) + private static readonly Assembly[] _assemblies; + + static TypeCache() + { + _assemblies = AppDomain.CurrentDomain.GetAssemblies() + .Where(x => x.FullName != "Microsoft.GeneratedCode, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") + .OrderBy(AssemblyOrderBy).ThenBy(x => x.FullName) .ToArray(); - private static int Order(Assembly assembly) + UnityEngine.Debug.Log("Assemblies:\n"+string.Join("\n", _assemblies.Select(x => x.FullName))); + _assemblyByName = new Dictionary(); + foreach (var assembly in _assemblies) + { + // Adding them like this because the LINQ ToDictionary does not like duplicate keys + _assemblyByName[assembly.GetName().Name] = assembly; + } + } + + private static int AssemblyOrderBy(Assembly assembly) { - if (assembly.FullName.StartsWith("Newtonsoft.Json")) return -1; - if (assembly.FullName.StartsWith("mscorlib")) return 1; - return 0; + var name = assembly.GetName().Name; + + // Newtonsoft.Json converters should be among the first, as they're most commonly referenced + if (name.StartsWith("Newtonsoft.Json")) + { + return -10; + } + + switch (GetRootNamespace(name)) + { + // User-defined code gets sent to the top + case "Assembly-CSharp": + return -1; + case "Assembly-CSharp-Editor": + return -2; + + // Unity standard library does not contain any converters + case "Unity": + return 10; + case "UnityEngine": + return 11; + case "UnityEditor": + return 12; + + // .NET standard library does not contain any converters, so just put it at the end + case "System": + return 20; + case "netstandard": + return 21; + case "mscorlib": + return 22; + + default: + return 0; + } } - public static Type FindType(string name) + private static string GetRootNamespace(string name) + { + int index = name.IndexOf('.'); + if (index > 0) + { + return name.Substring(0, index); + } + + return name; + } + + public static Type FindType(string name, string assemblyName) { if (_typeByName.TryGetValue(name, out var type)) { return type; } - else + + if (assemblyName != null) { - // Check this assembly, or if it has AssemblyQualifiedName - type = Type.GetType(name); - if (type != null) + if (_typeByNameAndAssembly.TryGetValue((name, assemblyName), out type)) { - _typeByName[name] = type; return type; } - // Check all the other assemblies, from last imported to first - foreach (var assembly in _assemblies) + if (_assemblyByName.TryGetValue(assemblyName, out var asm)) { - type = assembly.GetType(name); + type = asm.GetType(name); if (type != null) { - _typeByName[name] = type; + _typeByNameAndAssembly[(name, assemblyName)] = type; return type; } } - return null; } + + // Check this assembly, or if it has AssemblyQualifiedName + type = Type.GetType(name); + if (type != null) + { + _typeByName[name] = type; + return type; + } + + // Check all the other assemblies, from last imported to first + foreach (var assembly in _assemblies) + { + type = assembly.GetType(name); + if (type != null) + { + _typeByName[name] = type; + return type; + } + } + + return null; } } } From 23598740a6160cdf9d3ef0cf5c5b70e0d266ec41 Mon Sep 17 00:00:00 2001 From: Applejag Date: Thu, 29 Feb 2024 18:44:12 +0100 Subject: [PATCH 4/9] Remove Debug.Log & minor tweaks --- .../Utility/TypeCache.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs b/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs index 21e8ffe..e611d4f 100644 --- a/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs +++ b/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs @@ -23,7 +23,6 @@ static TypeCache() .OrderBy(AssemblyOrderBy).ThenBy(x => x.FullName) .ToArray(); - UnityEngine.Debug.Log("Assemblies:\n"+string.Join("\n", _assemblies.Select(x => x.FullName))); _assemblyByName = new Dictionary(); foreach (var assembly in _assemblies) { @@ -42,6 +41,7 @@ private static int AssemblyOrderBy(Assembly assembly) return -10; } + // Relies on the heuristic that "assembly name == namespace" switch (GetRootNamespace(name)) { // User-defined code gets sent to the top @@ -101,6 +101,7 @@ public static Type FindType(string name, string assemblyName) type = asm.GetType(name); if (type != null) { + _typeByName[name] = type; _typeByNameAndAssembly[(name, assemblyName)] = type; return type; } @@ -112,6 +113,7 @@ public static Type FindType(string name, string assemblyName) if (type != null) { _typeByName[name] = type; + _typeByNameAndAssembly[(name, type.Assembly.GetName().Name)] = type; return type; } @@ -122,6 +124,7 @@ public static Type FindType(string name, string assemblyName) if (type != null) { _typeByName[name] = type; + _typeByNameAndAssembly[(name, type.Assembly.GetName().Name)] = type; return type; } } From 50704b9988526352eaf6c2602c1e85c86264c9f7 Mon Sep 17 00:00:00 2001 From: George Gromov Date: Fri, 1 Mar 2024 10:42:53 +0300 Subject: [PATCH 5/9] Cache result of assembly.GetName() to reduce allocations --- .../Utility/TypeCache.cs | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs b/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs index e611d4f..2856696 100644 --- a/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs +++ b/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs @@ -18,31 +18,35 @@ private static readonly Dictionary _assemblyByName static TypeCache() { - _assemblies = AppDomain.CurrentDomain.GetAssemblies() - .Where(x => x.FullName != "Microsoft.GeneratedCode, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") - .OrderBy(AssemblyOrderBy).ThenBy(x => x.FullName) - .ToArray(); + var records = AppDomain.CurrentDomain.GetAssemblies() + .Select(x => new Record(x)) + .Where(x => x.AssemblyName != "Microsoft.GeneratedCode") + .OrderBy(AssemblyOrderBy).ThenBy(x => x.AssemblyName) + .ToList(); - _assemblyByName = new Dictionary(); - foreach (var assembly in _assemblies) + _assemblies = new Assembly[records.Count]; + _assemblyByName = new Dictionary(records.Count); + + for (int i = 0; i < records.Count; i++) { + var record = records[i]; + _assemblies[i] = record.Assembly; + // Adding them like this because the LINQ ToDictionary does not like duplicate keys - _assemblyByName[assembly.GetName().Name] = assembly; + _assemblyByName[record.AssemblyName] = record.Assembly; } } - private static int AssemblyOrderBy(Assembly assembly) + private static int AssemblyOrderBy(Record record) { - var name = assembly.GetName().Name; - // Newtonsoft.Json converters should be among the first, as they're most commonly referenced - if (name.StartsWith("Newtonsoft.Json")) + if (record.AssemblyName.StartsWith("Newtonsoft.Json")) { return -10; } // Relies on the heuristic that "assembly name == namespace" - switch (GetRootNamespace(name)) + switch (GetRootNamespace(record.AssemblyName)) { // User-defined code gets sent to the top case "Assembly-CSharp": @@ -131,5 +135,18 @@ public static Type FindType(string name, string assemblyName) return null; } + + + private readonly struct Record + { + public Assembly Assembly { get; } + public string AssemblyName { get; } + + public Record(Assembly assembly) + { + Assembly = assembly; + AssemblyName = assembly.GetName().Name; + } + } } } From 817768a1e19c85c55f38766b2b7244f4b360f348 Mon Sep 17 00:00:00 2001 From: Applejag Date: Fri, 1 Mar 2024 10:47:44 +0100 Subject: [PATCH 6/9] Rename types --- .../Utility/TypeCache.cs | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs b/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs index 2856696..1016788 100644 --- a/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs +++ b/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs @@ -18,35 +18,35 @@ private static readonly Dictionary _assemblyByName static TypeCache() { - var records = AppDomain.CurrentDomain.GetAssemblies() - .Select(x => new Record(x)) - .Where(x => x.AssemblyName != "Microsoft.GeneratedCode") - .OrderBy(AssemblyOrderBy).ThenBy(x => x.AssemblyName) + var assemblies = AppDomain.CurrentDomain.GetAssemblies() + .Select(x => new NamedAssembly(x)) + .Where(x => x.name != "Microsoft.GeneratedCode") + .OrderBy(AssemblyOrderBy).ThenBy(x => x.name) .ToList(); - _assemblies = new Assembly[records.Count]; - _assemblyByName = new Dictionary(records.Count); + _assemblies = new Assembly[assemblies.Count]; + _assemblyByName = new Dictionary(assemblies.Count); - for (int i = 0; i < records.Count; i++) + for (int i = 0; i < assemblies.Count; i++) { - var record = records[i]; - _assemblies[i] = record.Assembly; + NamedAssembly assembly = assemblies[i]; + _assemblies[i] = assembly.assembly; // Adding them like this because the LINQ ToDictionary does not like duplicate keys - _assemblyByName[record.AssemblyName] = record.Assembly; + _assemblyByName[assembly.name] = assembly.assembly; } } - private static int AssemblyOrderBy(Record record) + private static int AssemblyOrderBy(NamedAssembly record) { // Newtonsoft.Json converters should be among the first, as they're most commonly referenced - if (record.AssemblyName.StartsWith("Newtonsoft.Json")) + if (record.name.StartsWith("Newtonsoft.Json")) { return -10; } // Relies on the heuristic that "assembly name == namespace" - switch (GetRootNamespace(record.AssemblyName)) + switch (GetRootNamespace(record.name)) { // User-defined code gets sent to the top case "Assembly-CSharp": @@ -136,16 +136,15 @@ public static Type FindType(string name, string assemblyName) return null; } - - private readonly struct Record + private readonly struct NamedAssembly { - public Assembly Assembly { get; } - public string AssemblyName { get; } + public Assembly assembly { get; } + public string name { get; } - public Record(Assembly assembly) + public NamedAssembly(Assembly assembly) { - Assembly = assembly; - AssemblyName = assembly.GetName().Name; + this.assembly = assembly; + name = assembly.GetName().Name; } } } From 30b8e09847842ffea48fbd9897ea035c8e52d2f3 Mon Sep 17 00:00:00 2001 From: Applejag Date: Fri, 1 Mar 2024 10:50:33 +0100 Subject: [PATCH 7/9] Removed excess typeByName dict --- .../Utility/TypeCache.cs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs b/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs index 1016788..5c2be85 100644 --- a/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs +++ b/Packages/Newtonsoft.Json-for-Unity.Converters/Utility/TypeCache.cs @@ -7,8 +7,6 @@ namespace Newtonsoft.Json.UnityConverters.Utility { internal static class TypeCache { - private static readonly Dictionary _typeByName - = new Dictionary(); private static readonly Dictionary, Type> _typeByNameAndAssembly = new Dictionary, Type>(); private static readonly Dictionary _assemblyByName @@ -88,11 +86,7 @@ private static string GetRootNamespace(string name) public static Type FindType(string name, string assemblyName) { - if (_typeByName.TryGetValue(name, out var type)) - { - return type; - } - + Type type; if (assemblyName != null) { if (_typeByNameAndAssembly.TryGetValue((name, assemblyName), out type)) @@ -105,19 +99,24 @@ public static Type FindType(string name, string assemblyName) type = asm.GetType(name); if (type != null) { - _typeByName[name] = type; _typeByNameAndAssembly[(name, assemblyName)] = type; return type; } } } + else + { + if (_typeByNameAndAssembly.TryGetValue((name, null), out type)) + { + return type; + } + } // Check this assembly, or if it has AssemblyQualifiedName type = Type.GetType(name); if (type != null) { - _typeByName[name] = type; - _typeByNameAndAssembly[(name, type.Assembly.GetName().Name)] = type; + _typeByNameAndAssembly[(name, null)] = type; return type; } @@ -127,8 +126,7 @@ public static Type FindType(string name, string assemblyName) type = assembly.GetType(name); if (type != null) { - _typeByName[name] = type; - _typeByNameAndAssembly[(name, type.Assembly.GetName().Name)] = type; + _typeByNameAndAssembly[(name, null)] = type; return type; } } From 4d79272d9dff25054d5f579f68723a7702c14a6c Mon Sep 17 00:00:00 2001 From: Applejag Date: Fri, 1 Mar 2024 11:01:53 +0100 Subject: [PATCH 8/9] Updated CHANGELOG --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5b6cd7..f015054 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Unity Converters for Newtonsoft.Json changelog +## 1.6.3 (WIP) + +- Fixed converter lookups collisions when multiple assemblies converters + with the same name, as it was not resolving assemblies in an exact way + nor deterministic order: + + - Added assembly name field to `ConverterConfig` for each converter. + + - Changed TypeCache to sort assemblies based on `FullName` + and some heuristics. + + - Changed TypeCache to lookup type in correct assembly, + based on the assembly's name. + + Thanks [@Erifirin](https://github.com/Erifirin) for the pull request ([#90](https://github.com/jilleJr/Newtonsoft.Json-for-Unity.Converters/pull/90)) + ## 1.6.2 (2024-01-08) - Fixed typo in the new Unity.Mathematics QuaternionConverter's namespace: From cc4f54337d2cdf0f36a8f398e98b29ed3c02b196 Mon Sep 17 00:00:00 2001 From: Applejag Date: Fri, 1 Mar 2024 11:01:59 +0100 Subject: [PATCH 9/9] Updated version.json --- Build/version.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Build/version.json b/Build/version.json index 6e22d3e..3b5f4db 100644 --- a/Build/version.json +++ b/Build/version.json @@ -1,7 +1,7 @@ { "Major": 1, "Minor": 6, - "Patch": 2, + "Patch": 3, "Suffix": "", - "AutoDeployLiveRun": true + "AutoDeployLiveRun": false }