66
77using System ;
88using System . Collections . Concurrent ;
9+ using System . Collections . Generic ;
910using System . Linq ;
1011using System . Linq . Expressions ;
1112using System . Reflection ;
@@ -27,6 +28,10 @@ public static class ExpressionExtensions
2728
2829 private static readonly Func < Type , MethodInfo > TupleValueAccessorFactory ;
2930
31+ private static readonly Type MemoryExtensionsType = typeof ( MemoryExtensions ) ;
32+ private static readonly int [ ] MemoryExtensionsContainsMethodTokens ;
33+ private static readonly MethodInfo EnumerableContains ;
34+
3035 ///<summary>
3136 /// Makes <see cref="Tuples.Tuple.GetValueOrDefault{T}"/> method call.
3237 ///</summary>
@@ -72,6 +77,46 @@ public static Expression LiftToNullable(this Expression expression) =>
7277 /// <returns>Expression tree that wraps <paramref name="expression"/>.</returns>
7378 public static ExpressionTree ToExpressionTree ( this Expression expression ) => new ExpressionTree ( expression ) ;
7479
80+ /// <summary>
81+ /// Transforms <see cref="MemoryExtensions.Contains{T}(ReadOnlySpan{T}, T)"/> applied call into <see cref="Enumerable.Contains{TSource}(IEnumerable{TSource}, TSource)"/>
82+ /// if detected.
83+ /// </summary>
84+ /// <param name="mc">Possible candidate for transformation.</param>
85+ /// <returns>New instance of expression, if transformation was required, otherwise, the same expression.</returns>
86+ public static MethodCallExpression TryTransformToOldFashionContains ( this MethodCallExpression mc )
87+ {
88+ if ( mc . Method . DeclaringType == MemoryExtensionsType ) {
89+ var genericMethod = mc . Method . GetGenericMethodDefinition ( ) ;
90+ if ( MemoryExtensionsContainsMethodTokens . Contains ( genericMethod . MetadataToken ) ) {
91+ var arguments = mc . Arguments ;
92+
93+ Type elementType ;
94+ Expression [ ] newArguments ;
95+
96+ if ( arguments [ 0 ] is MethodCallExpression mcInner && mcInner . Method . Name . Equals ( WellKnown . Operator . Implicit , StringComparison . Ordinal ) ) {
97+ var wrappedArray = mcInner . Arguments [ 0 ] ;
98+ elementType = wrappedArray . Type . GetElementType ( ) ;
99+ newArguments = new [ ] { wrappedArray , arguments [ 1 ] } ;
100+ }
101+ else if ( arguments [ 0 ] is UnaryExpression uInner
102+ && uInner . Method is not null
103+ && uInner . Method . Name . Equals ( WellKnown . Operator . Implicit , StringComparison . Ordinal ) ) {
104+
105+ elementType = uInner . Operand . Type . GetElementType ( ) ;
106+ newArguments = new [ ] { uInner . Operand , arguments [ 1 ] } ;
107+ }
108+ else {
109+ return mc ;
110+ }
111+
112+ var genericContains = EnumerableContains . CachedMakeGenericMethod ( elementType ) ;
113+ var replacement = Expression . Call ( genericContains , newArguments ) ;
114+ return replacement ;
115+ }
116+ return mc ;
117+ }
118+ return mc ;
119+ }
75120
76121 // Type initializer
77122
@@ -80,6 +125,28 @@ static ExpressionExtensions()
80125 var tupleGenericAccessor = WellKnownOrmTypes . Tuple . GetMethods ( )
81126 . Single ( mi => mi . Name == nameof ( Tuple . GetValueOrDefault ) && mi . IsGenericMethod ) ;
82127 TupleValueAccessorFactory = type => tupleGenericAccessor . CachedMakeGenericMethod ( type ) ;
128+
129+ var genericReadOnlySpan = typeof ( ReadOnlySpan < > ) ;
130+ var genericSpan = typeof ( Span < > ) ;
131+
132+ var filteredByNameItems = MemoryExtensionsType . GetMethods ( BindingFlags . Public | BindingFlags . Static )
133+ . Where ( m => m . Name . Equals ( nameof ( System . MemoryExtensions . Contains ) , StringComparison . OrdinalIgnoreCase ) ) ;
134+
135+ var candiates = new List < int > ( ) ;
136+
137+ foreach ( var method in filteredByNameItems ) {
138+ var parameters = method . GetParameters ( ) ;
139+ var genericDef = parameters [ 0 ] . ParameterType . GetGenericTypeDefinition ( ) ;
140+ if ( genericDef == genericReadOnlySpan ) {
141+ if ( parameters . Length == 2 || parameters . Length == 3 )
142+ candiates . Add ( method . MetadataToken ) ;
143+ }
144+ else if ( genericDef == genericSpan && parameters . Length == 2 ) {
145+ candiates . Add ( method . MetadataToken ) ;
146+ }
147+ }
148+ MemoryExtensionsContainsMethodTokens = candiates . ToArray ( ) ;
149+ EnumerableContains = typeof ( System . Linq . Enumerable ) . GetMethodEx ( nameof ( System . Linq . Enumerable . Contains ) , BindingFlags . Public | BindingFlags . Static , new string [ 1 ] , new object [ 2 ] ) ;
83150 }
84151 }
85152}
0 commit comments