@@ -7,44 +7,142 @@ namespace Newtonsoft.Json.UnityConverters.Utility
7
7
{
8
8
internal static class TypeCache
9
9
{
10
- private static readonly Dictionary < string , Type > _typeByName
11
- = new Dictionary < string , Type > ( ) ;
12
-
13
- private static readonly Assembly [ ] _assemblies
14
- = AppDomain . CurrentDomain . GetAssemblies ( )
15
- // Reversing so we get last imported assembly first.
16
- // When searching for types we want to look in mscorlib last
17
- // and Newtonsoft.Json up as the first ones
18
- . Reverse ( )
19
- . ToArray ( ) ;
20
-
21
- public static Type FindType ( string name )
10
+ private static readonly Dictionary < ValueTuple < string , string > , Type > _typeByNameAndAssembly
11
+ = new Dictionary < ValueTuple < string , string > , Type > ( ) ;
12
+ private static readonly Dictionary < string , Assembly > _assemblyByName
13
+ = new Dictionary < string , Assembly > ( ) ;
14
+
15
+ private static readonly Assembly [ ] _assemblies ;
16
+
17
+ static TypeCache ( )
22
18
{
23
- if ( _typeByName . TryGetValue ( name , out var type ) )
19
+ var assemblies = AppDomain . CurrentDomain . GetAssemblies ( )
20
+ . Select ( x => new NamedAssembly ( x ) )
21
+ . Where ( x => x . name != "Microsoft.GeneratedCode" )
22
+ . OrderBy ( AssemblyOrderBy ) . ThenBy ( x => x . name )
23
+ . ToList ( ) ;
24
+
25
+ _assemblies = new Assembly [ assemblies . Count ] ;
26
+ _assemblyByName = new Dictionary < string , Assembly > ( assemblies . Count ) ;
27
+
28
+ for ( int i = 0 ; i < assemblies . Count ; i ++ )
24
29
{
25
- return type ;
30
+ NamedAssembly assembly = assemblies [ i ] ;
31
+ _assemblies [ i ] = assembly . assembly ;
32
+
33
+ // Adding them like this because the LINQ ToDictionary does not like duplicate keys
34
+ _assemblyByName [ assembly . name ] = assembly . assembly ;
26
35
}
27
- else
36
+ }
37
+
38
+ private static int AssemblyOrderBy ( NamedAssembly record )
39
+ {
40
+ // Newtonsoft.Json converters should be among the first, as they're most commonly referenced
41
+ if ( record . name . StartsWith ( "Newtonsoft.Json" ) )
28
42
{
29
- // Check this assembly, or if it has AssemblyQualifiedName
30
- type = Type . GetType ( name ) ;
31
- if ( type != null )
43
+ return - 10 ;
44
+ }
45
+
46
+ // Relies on the heuristic that "assembly name == namespace"
47
+ switch ( GetRootNamespace ( record . name ) )
48
+ {
49
+ // User-defined code gets sent to the top
50
+ case "Assembly-CSharp" :
51
+ return - 1 ;
52
+ case "Assembly-CSharp-Editor" :
53
+ return - 2 ;
54
+
55
+ // Unity standard library does not contain any converters
56
+ case "Unity" :
57
+ return 10 ;
58
+ case "UnityEngine" :
59
+ return 11 ;
60
+ case "UnityEditor" :
61
+ return 12 ;
62
+
63
+ // .NET standard library does not contain any converters, so just put it at the end
64
+ case "System" :
65
+ return 20 ;
66
+ case "netstandard" :
67
+ return 21 ;
68
+ case "mscorlib" :
69
+ return 22 ;
70
+
71
+ default :
72
+ return 0 ;
73
+ }
74
+ }
75
+
76
+ private static string GetRootNamespace ( string name )
77
+ {
78
+ int index = name . IndexOf ( '.' ) ;
79
+ if ( index > 0 )
80
+ {
81
+ return name . Substring ( 0 , index ) ;
82
+ }
83
+
84
+ return name ;
85
+ }
86
+
87
+ public static Type FindType ( string name , string assemblyName )
88
+ {
89
+ Type type ;
90
+ if ( assemblyName != null )
91
+ {
92
+ if ( _typeByNameAndAssembly . TryGetValue ( ( name , assemblyName ) , out type ) )
32
93
{
33
- _typeByName [ name ] = type ;
34
94
return type ;
35
95
}
36
96
37
- // Check all the other assemblies, from last imported to first
38
- foreach ( var assembly in _assemblies )
97
+ if ( _assemblyByName . TryGetValue ( assemblyName , out var asm ) )
39
98
{
40
- type = assembly . GetType ( name ) ;
99
+ type = asm . GetType ( name ) ;
41
100
if ( type != null )
42
101
{
43
- _typeByName [ name ] = type ;
102
+ _typeByNameAndAssembly [ ( name , assemblyName ) ] = type ;
44
103
return type ;
45
104
}
46
105
}
47
- return null ;
106
+ }
107
+ else
108
+ {
109
+ if ( _typeByNameAndAssembly . TryGetValue ( ( name , null ) , out type ) )
110
+ {
111
+ return type ;
112
+ }
113
+ }
114
+
115
+ // Check this assembly, or if it has AssemblyQualifiedName
116
+ type = Type . GetType ( name ) ;
117
+ if ( type != null )
118
+ {
119
+ _typeByNameAndAssembly [ ( name , null ) ] = type ;
120
+ return type ;
121
+ }
122
+
123
+ // Check all the other assemblies, from last imported to first
124
+ foreach ( var assembly in _assemblies )
125
+ {
126
+ type = assembly . GetType ( name ) ;
127
+ if ( type != null )
128
+ {
129
+ _typeByNameAndAssembly [ ( name , null ) ] = type ;
130
+ return type ;
131
+ }
132
+ }
133
+
134
+ return null ;
135
+ }
136
+
137
+ private readonly struct NamedAssembly
138
+ {
139
+ public Assembly assembly { get ; }
140
+ public string name { get ; }
141
+
142
+ public NamedAssembly ( Assembly assembly )
143
+ {
144
+ this . assembly = assembly ;
145
+ name = assembly . GetName ( ) . Name ;
48
146
}
49
147
}
50
148
}
0 commit comments