@@ -14,8 +14,22 @@ namespace Xtensive.Linq.SerializableExpressions.Internals
1414{
1515 internal sealed class SerializableExpressionToExpressionConverter
1616 {
17+ private readonly struct LambdaParameterScope : IDisposable
18+ {
19+ private readonly SerializableExpressionToExpressionConverter converter ;
20+
21+ public void Dispose ( ) => converter . parameterScopes . Pop ( ) ;
22+
23+ public LambdaParameterScope ( SerializableExpressionToExpressionConverter converter , Dictionary < string , ParameterExpression > currentScope )
24+ {
25+ this . converter = converter ;
26+ this . converter . parameterScopes . Push ( currentScope ) ;
27+ }
28+ }
29+
1730 private readonly SerializableExpression source ;
1831 private readonly Dictionary < SerializableExpression , Expression > cache ;
32+ private readonly Stack < Dictionary < string , ParameterExpression > > parameterScopes ;
1933
2034 public Expression Convert ( )
2135 {
@@ -137,12 +151,12 @@ private Expression VisitConstant(SerializableConstantExpression c)
137151
138152 private Expression VisitConditional ( SerializableConditionalExpression c )
139153 {
140- return Expression . Condition ( Visit ( c . Test ) , Visit ( c . IfTrue ) , Visit ( c . IfFalse ) ) ;
154+ return Expression . Condition ( Visit ( c . Test ) , Visit ( c . IfTrue ) , Visit ( c . IfFalse ) , c . Type ) ;
141155 }
142156
143157 private Expression VisitParameter ( SerializableParameterExpression p )
144158 {
145- return Expression . Parameter ( p . Type , p . Name ) ;
159+ return GetCachedParameter ( p . Type , p . Name ) ?? Expression . Parameter ( p . Type , p . Name ) ;
146160 }
147161
148162 private Expression VisitMemberAccess ( SerializableMemberExpression m )
@@ -167,7 +181,10 @@ private Expression VisitMethodCall(SerializableMethodCallExpression mc)
167181
168182 private Expression VisitLambda ( SerializableLambdaExpression l )
169183 {
170- return FastExpression . Lambda ( l . Type , Visit ( l . Body ) , l . Parameters . Select ( p => ( ParameterExpression ) Visit ( p ) ) ) ;
184+ var parameters = l . Parameters . Select ( p => ( ParameterExpression ) Visit ( p ) ) . ToList ( ) ;
185+ using ( CreateParameterScope ( parameters ) ) {
186+ return FastExpression . Lambda ( l . Type , Visit ( l . Body ) , parameters ) ;
187+ }
171188 }
172189
173190 private Expression VisitNew ( SerializableNewExpression n )
@@ -238,12 +255,49 @@ private IEnumerable<Expression> VisitExpressionSequence<T>(IEnumerable<T> expres
238255 return expressions . Select ( e => Visit ( e ) ) ;
239256 }
240257
258+ private LambdaParameterScope CreateParameterScope ( IReadOnlyList < ParameterExpression > lambdaParameters )
259+ {
260+ var parameters = new Dictionary < string , ParameterExpression > ( lambdaParameters . Count ) ;
261+ foreach ( var lambdaParameter in lambdaParameters ) {
262+ parameters . Add ( lambdaParameter . Name , lambdaParameter ) ;
263+ }
264+ return new LambdaParameterScope ( this , parameters ) ;
265+ }
266+
267+ private ParameterExpression GetCachedParameter ( Type type , string name )
268+ {
269+ var replacement = FindParameterFast ( type , name ) ;
270+ if ( replacement == null && parameterScopes . Count > 1 )
271+ return FindParameterSlow ( type , name ) ;
272+ return replacement ;
273+ }
274+
275+ private ParameterExpression FindParameterFast ( Type type , string name )
276+ {
277+ if ( parameterScopes . Count > 0 ) {
278+ var currentParameters = parameterScopes . Peek ( ) ;
279+ if ( currentParameters . TryGetValue ( name , out var replacement ) && replacement . Type == type )
280+ return replacement ;
281+ }
282+ return null ;
283+ }
284+
285+ private ParameterExpression FindParameterSlow ( Type type , string name )
286+ {
287+ foreach ( var scope in parameterScopes ) {
288+ if ( scope . TryGetValue ( name , out var replacement ) && replacement . Type == type )
289+ return replacement ;
290+ }
291+ return null ;
292+ }
293+
241294 #endregion
242295
243296 public SerializableExpressionToExpressionConverter ( SerializableExpression source )
244297 {
245298 this . source = source ;
246299 cache = new Dictionary < SerializableExpression , Expression > ( ) ;
300+ parameterScopes = new Stack < Dictionary < string , ParameterExpression > > ( ) ;
247301 }
248302 }
249303}
0 commit comments