Skip to content

Commit 2eabbe7

Browse files
committed
Handle certain deserialization problems
- After non-binary deserialization of SerializableLambdaExpression each parameter becomes unique object, which leads to incorrect lambdas. It is fixable only on conversion to Expressions because one lambda can use parameters from another lambda. - Consditional expression should also be created with type parameter
1 parent 8bbfe94 commit 2eabbe7

File tree

1 file changed

+57
-3
lines changed

1 file changed

+57
-3
lines changed

Orm/Xtensive.Orm/Linq/SerializableExpressions/Internals/SerializableExpressionToExpressionConverter.cs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)