@@ -17,21 +17,42 @@ namespace Xtensive.Orm.Linq.MemberCompilation
1717{
1818 internal partial class MemberCompilerProvider < T > : LockableBase , IMemberCompilerProvider < T >
1919 {
20- private MemberCompilerCollection compilers = new MemberCompilerCollection ( ) ;
20+ private readonly struct CompilerKey : IEquatable < CompilerKey >
21+ {
22+ private readonly Module module ;
23+ private readonly int metadataToken ;
24+
25+ public bool Equals ( CompilerKey other ) => metadataToken == other . metadataToken
26+ && ( ReferenceEquals ( module , other . module ) || module == other . module ) ;
27+
28+ public override bool Equals ( object obj ) => obj is CompilerKey other && Equals ( other ) ;
29+
30+ public override int GetHashCode ( )
31+ {
32+ unchecked {
33+ return module == null ? metadataToken : ( module . GetHashCode ( ) * 397 ) ^ metadataToken ;
34+ }
35+ }
2136
22- public Type ExpressionType { get { return typeof ( T ) ; } }
37+ public CompilerKey ( MemberInfo memberInfo )
38+ {
39+ module = memberInfo . Module ;
40+ metadataToken = memberInfo . MetadataToken ;
41+ }
42+ }
43+
44+ private readonly Dictionary < CompilerKey , Delegate > compilers
45+ = new Dictionary < CompilerKey , Delegate > ( ) ;
46+
47+ public Type ExpressionType => typeof ( T ) ;
2348
2449 public Delegate GetUntypedCompiler ( MemberInfo target )
2550 {
26- ArgumentValidator . EnsureArgumentNotNull ( target , "target" ) ;
27-
28- var actualTarget = GetCanonicalMember ( target ) ;
29- if ( actualTarget == null )
30- return null ;
31- var registration = compilers . Get ( actualTarget ) ;
32- if ( registration == null )
33- return null ;
34- return registration . CompilerInvoker ;
51+ ArgumentValidator . EnsureArgumentNotNull ( target , nameof ( target ) ) ;
52+
53+ return compilers . TryGetValue ( GetCompilerKey ( target ) , out var compiler )
54+ ? compiler
55+ : null ;
3556 }
3657
3758 public Func < T , T [ ] , T > GetCompiler ( MemberInfo target )
@@ -54,16 +75,11 @@ public void RegisterCompilers(Type compilerContainer, ConflictHandlingMethod con
5475 throw new InvalidOperationException ( string . Format (
5576 Strings . ExTypeXShouldNotBeGeneric , compilerContainer . GetFullName ( true ) ) ) ;
5677
57- var compilersToRegister = new MemberCompilerCollection ( ) ;
58-
5978 var compilerMethods = compilerContainer
6079 . GetMethods ( BindingFlags . Public | BindingFlags . Static | BindingFlags . DeclaredOnly )
6180 . Where ( method => method . IsDefined ( typeof ( CompilerAttribute ) , false ) && ! method . IsGenericMethod ) ;
6281
63- foreach ( var compiler in compilerMethods )
64- compilersToRegister . Add ( ProcessCompiler ( compiler ) ) ;
65-
66- UpdateRegistry ( compilersToRegister , conflictHandlingMethod ) ;
82+ UpdateRegistry ( compilerMethods . Select ( ProcessCompiler ) , conflictHandlingMethod ) ;
6783 }
6884
6985 public void RegisterCompilers ( IEnumerable < KeyValuePair < MemberInfo , Func < MemberInfo , T , T [ ] , T > > > compilerDefinitions )
@@ -76,71 +92,38 @@ public void RegisterCompilers(IEnumerable<KeyValuePair<MemberInfo, Func<MemberIn
7692 ArgumentValidator . EnsureArgumentNotNull ( compilerDefinitions , "compilerDefinitions" ) ;
7793 this . EnsureNotLocked ( ) ;
7894
79- var newItems = new MemberCompilerCollection ( ) ;
80- foreach ( var item in compilerDefinitions )
81- newItems . Add ( new MemberCompilerRegistration ( GetCanonicalMember ( item . Key ) , item . Value ) ) ;
95+ var newItems = compilerDefinitions . Select ( item => ( item . Key , ( Delegate ) item . Value ) ) ;
8296 UpdateRegistry ( newItems , conflictHandlingMethod ) ;
8397 }
8498
8599 #region Private methods
86100
87- private void UpdateRegistry ( MemberCompilerCollection newItems , ConflictHandlingMethod conflictHandlingMethod )
101+ private void UpdateRegistry (
102+ IEnumerable < ( MemberInfo targetMember , Delegate compiler ) > newRegistrations , ConflictHandlingMethod conflictHandlingMethod )
88103 {
89- if ( newItems . Count == 0 )
90- return ;
91- switch ( conflictHandlingMethod ) {
92- case ConflictHandlingMethod . KeepOld :
93- newItems . MergeWith ( compilers , false ) ;
94- compilers = newItems ;
95- break ;
96- case ConflictHandlingMethod . Overwrite :
97- compilers . MergeWith ( newItems , false ) ;
98- break ;
99- case ConflictHandlingMethod . ReportError :
100- compilers . MergeWith ( newItems , true ) ;
101- break ;
104+ foreach ( var ( targetMember , compiler ) in newRegistrations ) {
105+ var key = GetCompilerKey ( targetMember ) ;
106+ if ( conflictHandlingMethod != ConflictHandlingMethod . Overwrite && compilers . ContainsKey ( key ) ) {
107+ if ( conflictHandlingMethod == ConflictHandlingMethod . ReportError ) {
108+ throw new InvalidOperationException ( string . Format (
109+ Strings . ExCompilerForXIsAlreadyRegistered , targetMember . GetFullName ( true ) ) ) ;
110+ }
111+ continue ;
112+ }
113+ compilers [ key ] = compiler ;
102114 }
103115 }
104116
105- private static bool ParameterTypeMatches ( Type inputParameterType , Type candidateParameterType )
106- {
107- return inputParameterType . IsGenericParameter
108- ? candidateParameterType == inputParameterType
109- : ( candidateParameterType . IsGenericParameter || inputParameterType == candidateParameterType ) ;
110- }
111-
112- private static bool AllParameterTypesMatch (
113- IEnumerable < Type > inputParameterTypes , IEnumerable < Type > candidateParameterTypes )
114- {
115- return inputParameterTypes
116- . Zip ( candidateParameterTypes )
117- . All ( pair => ParameterTypeMatches ( pair . First , pair . Second ) ) ;
118- }
119-
120117 private static MethodBase GetCanonicalMethod ( MethodBase inputMethod , MethodBase [ ] possibleCanonicalMethods )
121118 {
122- var inputParameterTypes = inputMethod . GetParameterTypes ( ) ;
123-
124- var candidates = possibleCanonicalMethods
125- . Where ( candidate => candidate . Name == inputMethod . Name
126- && candidate . GetParameters ( ) . Length == inputParameterTypes . Length
127- && candidate . IsStatic == inputMethod . IsStatic )
128- . ToArray ( ) ;
129-
130- if ( candidates . Length == 0 )
131- return null ;
132- if ( candidates . Length == 1 )
133- return candidates [ 0 ] ;
134-
135- candidates = candidates
136- . Where ( candidate =>
137- AllParameterTypesMatch ( inputParameterTypes , candidate . GetParameterTypes ( ) ) )
138- . ToArray ( ) ;
139-
140- if ( candidates . Length != 1 )
141- return null ;
119+ foreach ( var candidate in possibleCanonicalMethods ) {
120+ if ( inputMethod . MetadataToken == candidate . MetadataToken
121+ && ( ReferenceEquals ( inputMethod . Module , candidate . Module ) || inputMethod . Module == candidate . Module ) ) {
122+ return candidate ;
123+ }
124+ }
142125
143- return candidates [ 0 ] ;
126+ return null ;
144127 }
145128
146129 private static Type [ ] ValidateCompilerParametersAndExtractTargetSignature ( MethodInfo compiler , bool requireMemberInfo )
@@ -176,7 +159,7 @@ private static Type[] ValidateCompilerParametersAndExtractTargetSignature(Method
176159 return result ;
177160 }
178161
179- private static MemberCompilerRegistration ProcessCompiler ( MethodInfo compiler )
162+ private static ( MemberInfo targetMember , Delegate compilerInvoker ) ProcessCompiler ( MethodInfo compiler )
180163 {
181164 var attribute = compiler . GetAttribute < CompilerAttribute > ( AttributeSearchOptions . InheritNone ) ;
182165
@@ -271,7 +254,7 @@ private static MemberCompilerRegistration ProcessCompiler(MethodInfo compiler)
271254 compiler . GetFullName ( true ) ) ) ;
272255
273256 var invoker = WrapInvoker ( CreateInvoker ( compiler , isStatic || isCtor , isGeneric ) ) ;
274- return new MemberCompilerRegistration ( targetMember , invoker ) ;
257+ return ( targetMember , invoker ) ;
275258 }
276259
277260 private static Func < MemberInfo , T , T [ ] , T > WrapInvoker ( Func < MemberInfo , T , T [ ] , T > invoker )
@@ -318,21 +301,18 @@ private static void ValidateCompilerParameter(ParameterInfo parameter, Type requ
318301 compiler . GetFullName ( true ) , parameter . Name , requiredType . GetFullName ( true ) ) ) ;
319302 }
320303
321- private static MemberInfo GetCanonicalMember ( MemberInfo member )
304+ private static CompilerKey GetCompilerKey ( MemberInfo member )
322305 {
323306 var canonicalMember = member ;
324307 var sourceProperty = canonicalMember as PropertyInfo ;
325308 if ( sourceProperty != null ) {
326309 canonicalMember = sourceProperty . GetGetMethod ( ) ;
327310 // GetGetMethod returns null in case of non public getter.
328- if ( canonicalMember == null )
329- return null ;
311+ if ( canonicalMember == null ) {
312+ return default ;
313+ }
330314 }
331315
332- var sourceMethod = canonicalMember as MethodInfo ;
333- if ( sourceMethod != null && sourceMethod . IsGenericMethod )
334- canonicalMember = sourceMethod . GetGenericMethodDefinition ( ) ;
335-
336316 var targetType = canonicalMember . ReflectedType ;
337317 if ( targetType . IsGenericType ) {
338318 targetType = targetType . GetGenericTypeDefinition ( ) ;
@@ -348,7 +328,7 @@ private static MemberInfo GetCanonicalMember(MemberInfo member)
348328 }
349329
350330 if ( canonicalMember == null ) {
351- return null ;
331+ return default ;
352332 }
353333
354334 if ( targetType . IsEnum ) {
@@ -359,7 +339,7 @@ private static MemberInfo GetCanonicalMember(MemberInfo member)
359339 canonicalMember = GetCanonicalMethod ( ( MethodInfo ) canonicalMember , targetType . GetMethods ( ) ) ;
360340 }
361341
362- return canonicalMember ;
342+ return new CompilerKey ( canonicalMember ) ;
363343 }
364344
365345 #endregion
0 commit comments