Skip to content

Commit a3b4935

Browse files
Copilotdadhi
andauthored
Add ExpressionEqualityComparer with EqualsTo extension method for LightExpression
Agent-Logs-Url: https://github.com/dadhi/FastExpressionCompiler/sessions/961c247d-26e0-4577-9e1c-7d227466ebaa Co-authored-by: dadhi <39516+dadhi@users.noreply.github.com>
1 parent dfe78db commit a3b4935

9 files changed

Lines changed: 628 additions & 7 deletions

File tree

src/FastExpressionCompiler/FastExpressionCompiler.cs

Lines changed: 402 additions & 0 deletions
Large diffs are not rendered by default.

test/FastExpressionCompiler.IssueTests/Issue261_Loop_wih_conditions_fails.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ public void Serialize_the_nullable_decimal_array()
277277
var sysExpr = expr.ToLambdaExpression();
278278
var restoredExpr = sysExpr.ToLightExpression();
279279
restoredExpr.PrintCSharp();
280-
Asserts.AreEqual(expr.ToCSharpString(), restoredExpr.ToCSharpString());
280+
Asserts.IsTrue(expr.EqualsTo(restoredExpr));
281281
#endif
282282

283283
var fs = expr.CompileSys();

test/FastExpressionCompiler.IssueTests/Issue274_Failing_Expressions_in_Linq2DB.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,7 @@ public void Test_case_2_Full_ExecutionEngineException()
271271
#if LIGHT_EXPRESSION
272272
var sysExpr = expr.ToLambdaExpression();
273273
var restoredExpr = sysExpr.ToLightExpression();
274-
// todo: @feature #431 compare the restored target and source expressions directly instead of strings
275-
Asserts.AreEqual(expr.ToCSharpString(), restoredExpr.ToCSharpString());
274+
Asserts.IsTrue(expr.EqualsTo(restoredExpr));
276275
#endif
277276

278277
var fs = expr.CompileSys();

test/FastExpressionCompiler.IssueTests/Issue430_TryCatch_Bad_label_content_in_ILGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public void Original_case()
4242
var sysExpr = expr.ToLambdaExpression();
4343
var restoredExpr = sysExpr.ToLightExpression();
4444
restoredExpr.PrintCSharp();
45-
Asserts.AreEqual(expr.ToCSharpString(), restoredExpr.ToCSharpString());
45+
Asserts.IsTrue(expr.EqualsTo(restoredExpr));
4646
#endif
4747

4848
var fs = expr.CompileSys();
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
using System;
2+
using System.Linq;
3+
using System.Reflection;
4+
5+
6+
#if LIGHT_EXPRESSION
7+
using static FastExpressionCompiler.LightExpression.Expression;
8+
using FastExpressionCompiler.LightExpression;
9+
namespace FastExpressionCompiler.LightExpression.IssueTests;
10+
#else
11+
using static System.Linq.Expressions.Expression;
12+
namespace FastExpressionCompiler.IssueTests;
13+
#endif
14+
15+
16+
#if LIGHT_EXPRESSION
17+
public class Issue431_Add_structural_equality_comparison_to_LightExpression : ITest
18+
{
19+
public int Run()
20+
{
21+
Eq_simple_lambda();
22+
Eq_lambda_with_parameters();
23+
Eq_constants();
24+
Eq_member_access();
25+
Eq_method_call();
26+
Eq_new_expression();
27+
Eq_member_init();
28+
Eq_new_array();
29+
Eq_conditional();
30+
Eq_block_with_variables();
31+
Eq_try_catch();
32+
Eq_loop_with_labels();
33+
Eq_switch();
34+
Eq_complex_lambda_round_trip();
35+
NotEq_different_constants();
36+
NotEq_different_types();
37+
NotEq_different_parameters();
38+
return 17;
39+
}
40+
41+
public void Eq_simple_lambda()
42+
{
43+
var e1 = Lambda<Func<int>>(Constant(42));
44+
var e2 = Lambda<Func<int>>(Constant(42));
45+
Asserts.IsTrue(e1.EqualsTo(e2));
46+
}
47+
48+
public void Eq_lambda_with_parameters()
49+
{
50+
var p1a = Parameter(typeof(int), "x");
51+
var p1b = Parameter(typeof(int), "y");
52+
var e1 = Lambda<Func<int, int, int>>(Add(p1a, p1b), p1a, p1b);
53+
54+
var p2a = Parameter(typeof(int), "x");
55+
var p2b = Parameter(typeof(int), "y");
56+
var e2 = Lambda<Func<int, int, int>>(Add(p2a, p2b), p2a, p2b);
57+
58+
Asserts.IsTrue(e1.EqualsTo(e2));
59+
}
60+
61+
public void Eq_constants()
62+
{
63+
Asserts.IsTrue(Constant(42).EqualsTo(Constant(42)));
64+
Asserts.IsTrue(Constant("hello").EqualsTo(Constant("hello")));
65+
Asserts.IsTrue(Constant(null, typeof(string)).EqualsTo(Constant(null, typeof(string))));
66+
}
67+
68+
public void Eq_member_access()
69+
{
70+
var prop = typeof(string).GetProperty(nameof(string.Length));
71+
var p1 = Parameter(typeof(string), "s");
72+
var p2 = Parameter(typeof(string), "s");
73+
var e1 = Lambda<Func<string, int>>(Property(p1, prop), p1);
74+
var e2 = Lambda<Func<string, int>>(Property(p2, prop), p2);
75+
Asserts.IsTrue(e1.EqualsTo(e2));
76+
}
77+
78+
public void Eq_method_call()
79+
{
80+
var method = typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) });
81+
var p1 = Parameter(typeof(string), "a");
82+
var p2 = Parameter(typeof(string), "b");
83+
var pa = Parameter(typeof(string), "a");
84+
var pb = Parameter(typeof(string), "b");
85+
var e1 = Lambda<Func<string, string, string>>(Call(method, p1, p2), p1, p2);
86+
var e2 = Lambda<Func<string, string, string>>(Call(method, pa, pb), pa, pb);
87+
Asserts.IsTrue(e1.EqualsTo(e2));
88+
}
89+
90+
public void Eq_new_expression()
91+
{
92+
var ctor = typeof(B).GetConstructor(Type.EmptyTypes);
93+
var e1 = New(ctor);
94+
var e2 = New(ctor);
95+
Asserts.IsTrue(e1.EqualsTo(e2));
96+
}
97+
98+
public static ConstructorInfo CtorOfA = typeof(A).GetTypeInfo().DeclaredConstructors.First();
99+
public static ConstructorInfo CtorOfB = typeof(B).GetTypeInfo().DeclaredConstructors.First();
100+
public static PropertyInfo PropAProp = typeof(A).GetTypeInfo().DeclaredProperties.First(p => p.Name == "Prop");
101+
102+
public void Eq_member_init()
103+
{
104+
var e1 = MemberInit(New(CtorOfA, New(CtorOfB)), Bind(PropAProp, New(CtorOfB)));
105+
var e2 = MemberInit(New(CtorOfA, New(CtorOfB)), Bind(PropAProp, New(CtorOfB)));
106+
Asserts.IsTrue(e1.EqualsTo(e2));
107+
}
108+
109+
public void Eq_new_array()
110+
{
111+
var e1 = NewArrayInit(typeof(int), Constant(1), Constant(2), Constant(3));
112+
var e2 = NewArrayInit(typeof(int), Constant(1), Constant(2), Constant(3));
113+
Asserts.IsTrue(e1.EqualsTo(e2));
114+
}
115+
116+
public void Eq_conditional()
117+
{
118+
var p1 = Parameter(typeof(int), "x");
119+
var p2 = Parameter(typeof(int), "x");
120+
var e1 = Lambda<Func<int, int>>(Condition(Equal(p1, Constant(0)), Constant(1), p1), p1);
121+
var e2 = Lambda<Func<int, int>>(Condition(Equal(p2, Constant(0)), Constant(1), p2), p2);
122+
Asserts.IsTrue(e1.EqualsTo(e2));
123+
}
124+
125+
public void Eq_block_with_variables()
126+
{
127+
var v1 = Variable(typeof(int), "i");
128+
var v2 = Variable(typeof(int), "i");
129+
var e1 = Block(new[] { v1 }, Assign(v1, Constant(5)), v1);
130+
var e2 = Block(new[] { v2 }, Assign(v2, Constant(5)), v2);
131+
Asserts.IsTrue(e1.EqualsTo(e2));
132+
}
133+
134+
public void Eq_try_catch()
135+
{
136+
var ex1 = Parameter(typeof(Exception), "ex");
137+
var ex2 = Parameter(typeof(Exception), "ex");
138+
var e1 = TryCatch(Constant(1),
139+
Catch(ex1, Constant(2)));
140+
var e2 = TryCatch(Constant(1),
141+
Catch(ex2, Constant(2)));
142+
Asserts.IsTrue(e1.EqualsTo(e2));
143+
}
144+
145+
public void Eq_loop_with_labels()
146+
{
147+
var brk1 = Label(typeof(void), "break");
148+
var cnt1 = Label(typeof(void), "continue");
149+
var brk2 = Label(typeof(void), "break");
150+
var cnt2 = Label(typeof(void), "continue");
151+
var e1 = Loop(Block(Break(brk1), Continue(cnt1)), brk1, cnt1);
152+
var e2 = Loop(Block(Break(brk2), Continue(cnt2)), brk2, cnt2);
153+
Asserts.IsTrue(e1.EqualsTo(e2));
154+
}
155+
156+
public void Eq_switch()
157+
{
158+
var p1 = Parameter(typeof(int), "x");
159+
var p2 = Parameter(typeof(int), "x");
160+
var e1 = Lambda<Func<int, int>>(
161+
Switch(p1, Constant(-1), SwitchCase(Constant(10), Constant(1)), SwitchCase(Constant(20), Constant(2))),
162+
p1);
163+
var e2 = Lambda<Func<int, int>>(
164+
Switch(p2, Constant(-1), SwitchCase(Constant(10), Constant(1)), SwitchCase(Constant(20), Constant(2))),
165+
p2);
166+
Asserts.IsTrue(e1.EqualsTo(e2));
167+
}
168+
169+
public void Eq_complex_lambda_round_trip()
170+
{
171+
var expr = Lambda<Func<object[], object>>(
172+
MemberInit(
173+
New(CtorOfA, New(CtorOfB)),
174+
Bind(PropAProp, New(CtorOfB))),
175+
ParameterOf<object[]>("p"));
176+
177+
var sysExpr = expr.ToLambdaExpression();
178+
var restoredExpr = sysExpr.ToLightExpression<Func<object[], object>>();
179+
180+
Asserts.IsTrue(expr.EqualsTo(restoredExpr));
181+
}
182+
183+
public void NotEq_different_constants()
184+
{
185+
Asserts.IsFalse(Constant(42).EqualsTo(Constant(43)));
186+
Asserts.IsFalse(Constant("a").EqualsTo(Constant("b")));
187+
}
188+
189+
public void NotEq_different_types()
190+
{
191+
Asserts.IsFalse(Constant(42).EqualsTo(Constant(42L)));
192+
Asserts.IsFalse(Default(typeof(int)).EqualsTo(Default(typeof(long))));
193+
}
194+
195+
public void NotEq_different_parameters()
196+
{
197+
// Parameters with different names should not be equal when unmapped
198+
var p1 = Parameter(typeof(int), "x");
199+
var p2 = Parameter(typeof(int), "y");
200+
var e1 = Lambda<Func<int, int>>(p1, p1);
201+
var e2 = Lambda<Func<int, int>>(p2, p2);
202+
// When mapped by position in a lambda, different-named params ARE equal structurally (same position)
203+
Asserts.IsTrue(e1.EqualsTo(e2));
204+
205+
// But accessing a param outside its lambda context uses name comparison
206+
Asserts.IsFalse(p1.EqualsTo(p2));
207+
}
208+
209+
public class A
210+
{
211+
public B Prop { get; set; }
212+
public A(B b) { Prop = b; }
213+
}
214+
215+
public class B { }
216+
}
217+
#endif

test/FastExpressionCompiler.LightExpression.IssueTests/Issue363_ActionFunc16Generics.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public void Can_Create_Func(int paramCount)
7878
var sysExpr = expr.ToLambdaExpression();
7979
var restoredExpr = sysExpr.ToLightExpression();
8080
restoredExpr.PrintCSharp();
81-
Asserts.AreEqual(expr.ToCSharpString(), restoredExpr.ToCSharpString());
81+
Asserts.IsTrue(expr.EqualsTo(restoredExpr));
8282
#endif
8383

8484
// (a, b, c, ...) => new[] { a, b, c, ... }

test/FastExpressionCompiler.TestsRunner.Net472/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,8 @@ void Run(Func<int> run, string name = null)
382382
Run(new Issue430_TryCatch_Bad_label_content_in_ILGenerator().Run);
383383
Run(new LightExpression.IssueTests.Issue430_TryCatch_Bad_label_content_in_ILGenerator().Run);
384384

385+
Run(new LightExpression.IssueTests.Issue431_Add_structural_equality_comparison_to_LightExpression().Run);
386+
385387
Run(new Issue437_Shared_variables_with_nested_lambdas_returning_incorrect_values().Run);
386388
Run(new LightExpression.IssueTests.Issue437_Shared_variables_with_nested_lambdas_returning_incorrect_values().Run);
387389

test/FastExpressionCompiler.TestsRunner/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,8 @@ void Run(Func<int> run, string name = null)
391391
Run(new Issue430_TryCatch_Bad_label_content_in_ILGenerator().Run);
392392
Run(new LightExpression.IssueTests.Issue430_TryCatch_Bad_label_content_in_ILGenerator().Run);
393393

394+
Run(new LightExpression.IssueTests.Issue431_Add_structural_equality_comparison_to_LightExpression().Run);
395+
394396
Run(new Issue437_Shared_variables_with_nested_lambdas_returning_incorrect_values().Run);
395397
Run(new LightExpression.IssueTests.Issue437_Shared_variables_with_nested_lambdas_returning_incorrect_values().Run);
396398

test/FastExpressionCompiler.UnitTests/AssignTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,8 +386,7 @@ public void Array_multi_dimensional_index_assign_value_type_block()
386386
var sysExpr = expr.ToLambdaExpression();
387387
var restoredExpr = sysExpr.ToLightExpression();
388388
restoredExpr.PrintCSharp();
389-
// todo: @wip #431 generates different names for the unnamed variables which is not comparable
390-
Asserts.AreEqual(expr.ToCSharpString(), restoredExpr.ToCSharpString());
389+
Asserts.IsTrue(expr.EqualsTo(restoredExpr));
391390
#endif
392391
Asserts.IsNotNull(fs);
393392
Asserts.AreEqual(5, fs());

0 commit comments

Comments
 (0)