Skip to content

Commit 7c3f106

Browse files
committed
ConstructorExpression: No many clones of empty collection
1 parent 454b937 commit 7c3f106

File tree

4 files changed

+51
-12
lines changed

4 files changed

+51
-12
lines changed

Orm/Xtensive.Orm/Orm/Linq/Expressions/ConstructorExpression.cs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using Xtensive.Orm.Linq.Expressions;
1515
using Xtensive.Orm.Linq.Expressions.Visitors;
1616
using System.Linq;
17+
using System.Net.Http.Headers;
1718

1819
namespace Xtensive.Orm.Linq
1920
{
@@ -33,24 +34,30 @@ public Expression BindParameter(ParameterExpression parameter, Dictionary<Expres
3334
{
3435
Func<Expression, Expression> genericBinder =
3536
e => GenericExpressionVisitor<IMappedExpression>.Process(e, mapped => mapped.BindParameter(parameter, processedExpressions));
37+
3638
return new ConstructorExpression(
3739
Type,
3840
Bindings.ToDictionary(kvp => kvp.Key, kvp => genericBinder(kvp.Value), Bindings.Count),
3941
NativeBindings.ToDictionary(kvp=>kvp.Key, kvp => genericBinder(kvp.Value), NativeBindings.Count),
4042
Constructor,
41-
ConstructorArguments.Select(genericBinder).ToArray());
43+
ReferenceEquals(ConstructorArguments, Array.Empty<Expression>())
44+
? ConstructorArguments
45+
: ConstructorArguments.Select(genericBinder).ToArray());
4246
}
4347

4448
public Expression RemoveOuterParameter(Dictionary<Expression, Expression> processedExpressions)
4549
{
4650
Func<Expression, Expression> genericRemover =
4751
e => GenericExpressionVisitor<IMappedExpression>.Process(e, mapped => mapped.RemoveOuterParameter(processedExpressions));
52+
4853
var result = new ConstructorExpression(
4954
Type,
5055
Bindings.ToDictionary(kvp => kvp.Key, kvp => genericRemover(kvp.Value)),
5156
NativeBindings = NativeBindings.ToDictionary(kvp => kvp.Key, kvp => genericRemover(kvp.Value)),
5257
Constructor,
53-
ConstructorArguments.Select(genericRemover).ToArray());
58+
ReferenceEquals(ConstructorArguments, Array.Empty<Expression>())
59+
? ConstructorArguments
60+
: ConstructorArguments.Select(genericRemover).ToArray());
5461
return result;
5562
}
5663

@@ -62,8 +69,11 @@ public Expression Remap(int offset, Dictionary<Expression, Expression> processed
6269
return mapped.Remap(offset, new Dictionary<Expression, Expression>());
6370
return (Expression) mapped;
6471
};
72+
6573
var newBindings = Bindings.ToDictionary(kvp => kvp.Key, kvp => GenericExpressionVisitor<IMappedExpression>.Process(kvp.Value, remapper));
66-
var newConstructorArguments = ConstructorArguments.Select(arg => GenericExpressionVisitor<IMappedExpression>.Process(arg, remapper));
74+
var newConstructorArguments = ReferenceEquals(ConstructorArguments, Array.Empty<Expression>())
75+
? ConstructorArguments
76+
: ConstructorArguments.Select(arg => GenericExpressionVisitor<IMappedExpression>.Process(arg, remapper));
6777
var newNativeBindings = NativeBindings.ToDictionary(kvp => kvp.Key, kvp => GenericExpressionVisitor<IMappedExpression>.Process(kvp.Value, remapper));
6878
var result = new ConstructorExpression(
6979
Type,
@@ -82,18 +92,27 @@ public Expression Remap(IReadOnlyList<int> map, Dictionary<Expression, Expressio
8292
return mapped.Remap(map, new Dictionary<Expression, Expression>());
8393
return (Expression) mapped;
8494
};
95+
8596
var newBindings = Bindings.ToDictionary(kvp => kvp.Key, kvp => GenericExpressionVisitor<IMappedExpression>.Process(kvp.Value, remapper));
86-
var newConstructorArguments = ConstructorArguments.Select(arg => GenericExpressionVisitor<IMappedExpression>.Process(arg, remapper));
97+
var newConstructorArguments = ReferenceEquals(ConstructorArguments, Array.Empty<Expression>())
98+
? ConstructorArguments
99+
: ConstructorArguments.Select(arg => GenericExpressionVisitor<IMappedExpression>.Process(arg, remapper));
87100
var newNativeBindings = NativeBindings.ToDictionary(kvp => kvp.Key, kvp => GenericExpressionVisitor<IMappedExpression>.Process(kvp.Value, remapper));
88101
return new ConstructorExpression(Type, newBindings, newNativeBindings, Constructor, newConstructorArguments);
89102
}
90103

91-
public ConstructorExpression(Type type, Dictionary<MemberInfo, Expression> bindings, Dictionary<MemberInfo, Expression> nativeBindings, ConstructorInfo constructor, IEnumerable<Expression> constructorArguments)
104+
public ConstructorExpression(Type type,
105+
Dictionary<MemberInfo, Expression> bindings,
106+
Dictionary<MemberInfo, Expression> nativeBindings,
107+
ConstructorInfo constructor,
108+
IEnumerable<Expression> constructorArguments)
92109
: base(ExtendedExpressionType.Constructor, type, null, false)
93110
{
94111
Bindings = bindings ?? new Dictionary<MemberInfo, Expression>();
95112
NativeBindings = nativeBindings;
96-
ConstructorArguments = constructorArguments ?? Enumerable.Empty<Expression>();
113+
// consistently use keep empty array instead of any other empty collection,
114+
// there are checks that help to not instanciate collections
115+
ConstructorArguments = constructorArguments ?? Array.Empty<Expression>();
97116
Constructor = constructor;
98117
}
99118
}

Orm/Xtensive.Orm/Orm/Linq/Expressions/Visitors/ExtendedExpressionReplacer.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
using System;
88
using System.Collections.Generic;
9+
using System.Linq;
910
using System.Linq.Expressions;
1011
using System.Reflection;
1112
using Xtensive.Orm.Rse.Providers;
@@ -115,7 +116,7 @@ protected override ColumnExpression VisitColumnExpression(ColumnExpression expre
115116

116117
protected override ConstructorExpression VisitConstructorExpression(ConstructorExpression expression)
117118
{
118-
var arguments = new List<Expression>();
119+
IList<Expression> arguments = new List<Expression>();
119120
var bindings = new Dictionary<MemberInfo, Expression>(expression.Bindings.Count);
120121
var nativeBindings = new Dictionary<MemberInfo, Expression>(expression.NativeBindings.Count);
121122
bool recreate = false;
@@ -144,7 +145,7 @@ protected override ConstructorExpression VisitConstructorExpression(ConstructorE
144145
bindings,
145146
nativeBindings,
146147
expression.Constructor,
147-
arguments);
148+
arguments.Count > 0 ? arguments : Array.Empty<Expression>());
148149
}
149150

150151
protected override MarkerExpression VisitMarker(MarkerExpression expression)

Orm/Xtensive.Orm/Orm/Linq/Expressions/Visitors/OwnerRemover.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
// Created by: Alexis Kochetov
55
// Created: 2009.05.26
66

7+
using System;
8+
using System.Collections.Generic;
79
using System.Linq;
810
using System.Linq.Expressions;
911
using Xtensive.Core;
@@ -45,8 +47,23 @@ protected override KeyExpression VisitKeyExpression(KeyExpression expression)
4547

4648
protected override ConstructorExpression VisitConstructorExpression(ConstructorExpression expression)
4749
{
48-
var oldConstructorArguments = expression.ConstructorArguments.ToList();
49-
var newConstructorArguments = VisitExpressionList(oldConstructorArguments);
50+
IReadOnlyList<Expression> oldConstructorArguments;
51+
IReadOnlyList<Expression> newConstructorArguments;
52+
53+
if (ReferenceEquals(expression.ConstructorArguments, Array.Empty<Expression>())) {
54+
oldConstructorArguments = newConstructorArguments = Array.Empty<Expression>();
55+
}
56+
else if (expression.ConstructorArguments is IReadOnlyList<Expression> argsAsList) {
57+
oldConstructorArguments = argsAsList;
58+
newConstructorArguments = VisitExpressionList(argsAsList); // creates a copy internally
59+
}
60+
else {
61+
oldConstructorArguments = expression.ConstructorArguments.ToList();
62+
if (oldConstructorArguments.Count == 0)
63+
oldConstructorArguments = newConstructorArguments = Array.Empty<Expression>();
64+
else
65+
newConstructorArguments = VisitExpressionList(oldConstructorArguments);
66+
}
5067

5168
var oldBindings = expression.Bindings.SelectToList(b => b.Value);
5269
var newBindings = VisitExpressionList(oldBindings);

Orm/Xtensive.Orm/Orm/Linq/Translator.Expressions.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -690,9 +690,11 @@ protected override Expression VisitNew(NewExpression newExpression)
690690
// ReSharper restore ConditionIsAlwaysTrueOrFalse
691691
// ReSharper restore HeuristicUnreachableCode
692692

693-
var arguments = VisitNewExpressionArguments(newExpression);
693+
IList<Expression> arguments = VisitNewExpressionArguments(newExpression);
694+
if (arguments.Count == 0)
695+
arguments = Array.Empty<Expression>();
694696
if (newExpression.IsAnonymousConstructor()) {
695-
return newExpression.Members==null
697+
return newExpression.Members == null
696698
? Expression.New(newExpression.Constructor, arguments)
697699
: Expression.New(newExpression.Constructor, arguments, newExpression.Members);
698700
}

0 commit comments

Comments
 (0)