Skip to content

Commit 6c5d555

Browse files
authored
Merge pull request #314 from exyi/fix-toexpr-lifttonull
Fix bug in ToExpressionString with lifted binary expressions
2 parents 2b4127c + c1d64f4 commit 6c5d555

File tree

4 files changed

+140
-23
lines changed

4 files changed

+140
-23
lines changed

src/FastExpressionCompiler/FastExpressionCompiler.cs

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5626,9 +5626,12 @@ public static string ToExpressionString(this Expression expr,
56265626
lts = new List<LabelTarget>();
56275627
sb = expr.CreateExpressionString(sb, paramsExprs, uniqueExprs, lts, 2, stripNamespace, printType, identSpaces, tryPrintConstant).Append(';');
56285628

5629-
sb.Insert(0, $"var l = new LabelTarget[{lts.Count}]; // the labels {NewLine}");
5630-
sb.Insert(0, $"var e = new Expression[{uniqueExprs.Count}]; // the unique expressions {NewLine}");
5631-
sb.Insert(0, $"var p = new ParameterExpression[{paramsExprs.Count}]; // the parameter expressions {NewLine}");
5629+
if (lts.Count > 0)
5630+
sb.Insert(0, $"var l = new LabelTarget[{lts.Count}]; // the labels {NewLine}");
5631+
if (uniqueExprs.Count > 0)
5632+
sb.Insert(0, $"var e = new Expression[{uniqueExprs.Count}]; // the unique expressions {NewLine}");
5633+
if (paramsExprs.Count > 0)
5634+
sb.Insert(0, $"var p = new ParameterExpression[{paramsExprs.Count}]; // the parameter expressions {NewLine}");
56325635

56335636
return sb.ToString();
56345637
}
@@ -5818,21 +5821,9 @@ internal static StringBuilder CreateExpressionString(this Expression e, StringBu
58185821
sb.AppendTypeof(t, stripNamespace, printType);
58195822
else
58205823
{
5821-
// For the closure bound constant let's output `null` or default value with the comment for user to provide the actual value
5822-
if (ExpressionCompiler.IsClosureBoundConstant(x.Value, x.Type))
5823-
{
5824-
if (x.Type.IsValueType)
5825-
sb.Append("default(").Append(x.Type.ToCode(stripNamespace, printType)).Append(')');
5826-
else // specifying the type for the Constant, otherwise we will lost it with the `Constant(default(MyClass))` which is equivalent to `Constant(null)`
5827-
sb.Append("null, ").AppendTypeof(x.Type, stripNamespace, printType);
5828-
sb.NewLineIdent(lineIdent).Append("// !!! Please provide the non-default value").NewLineIdent(lineIdent);
5829-
}
5830-
else
5831-
{
5832-
sb.Append(x.Value.ToCode(CodePrinter.DefaultConstantValueToCode, stripNamespace, printType));
5833-
if (x.Value.GetType() != x.Type)
5834-
sb.Append(", ").AppendTypeof(x.Type, stripNamespace, printType);
5835-
}
5824+
sb.Append(x.Value.ToCode(CodePrinter.DefaultConstantValueToCode, stripNamespace, printType));
5825+
if (x.Value.GetType() != x.Type)
5826+
sb.Append(", ").AppendTypeof(x.Type, stripNamespace, printType);
58365827
}
58375828
return sb.Append(')');
58385829
}
@@ -6121,6 +6112,15 @@ internal static StringBuilder CreateExpressionString(this Expression e, StringBu
61216112
sb.Append("MakeBinary(").Append(typeof(ExpressionType).Name).Append('.').Append(name).Append(',');
61226113
sb.NewLineIdentExpr(b.Left, paramsExprs, uniqueExprs, lts, lineIdent, stripNamespace, printType, identSpaces, tryPrintConstant).Append(',');
61236114
sb.NewLineIdentExpr(b.Right, paramsExprs, uniqueExprs, lts, lineIdent, stripNamespace, printType, identSpaces, tryPrintConstant);
6115+
if (b.IsLiftedToNull || b.Method != null || b.Conversion != null)
6116+
{
6117+
sb.Append(',').NewLineIdent(lineIdent).Append("liftToNull: ").Append(b.IsLiftedToNull.ToCode()).Append(',');
6118+
sb.NewLineIdent(lineIdent).AppendMethod(b.Method, stripNamespace, printType);
6119+
}
6120+
if (b.Conversion != null)
6121+
{
6122+
sb.Append(',').NewLineIdentExpr(b.Conversion, paramsExprs, uniqueExprs, lts, lineIdent, stripNamespace, printType, identSpaces, tryPrintConstant);
6123+
}
61246124
}
61256125
return sb.Append(')');
61266126
}
@@ -7074,6 +7074,12 @@ public static string ToCode(this Type type,
70747074
return !printGenericTypeArgs ? string.Empty
70757075
: (printType?.Invoke(type, type.Name) ?? type.Name);
70767076

7077+
if (Nullable.GetUnderlyingType(type) is Type nullableElementType && !type.IsGenericTypeDefinition)
7078+
{
7079+
var result = nullableElementType.ToCode(stripNamespace, printType, printGenericTypeArgs) + "?";
7080+
return printType?.Invoke(type, result) ?? result;
7081+
}
7082+
70777083
Type arrayType = null;
70787084
if (type.IsArray)
70797085
{
@@ -7246,7 +7252,7 @@ public interface IObjectToCode
72467252
private class ConstantValueToCode : CodePrinter.IObjectToCode
72477253
{
72487254
public string ToCode(object x, bool stripNamespace = false, Func<Type, string, string> printType = null) =>
7249-
"default(" + x.GetType().ToCode(stripNamespace, printType) + ")";
7255+
"default(" + x.GetType().ToCode(stripNamespace, printType) + ") /* " + x.ToString() + " !!! Please provide the non-default value */ ";
72507256
}
72517257

72527258

@@ -7289,6 +7295,12 @@ public static string ToCode(this object x, IObjectToCode notRecognizedToCode,
72897295
if (x is bool b)
72907296
return b.ToCode();
72917297

7298+
if (x is int i)
7299+
return i.ToString();
7300+
7301+
if (x is double d)
7302+
return d.ToString();
7303+
72927304
if (x is string s)
72937305
return s.ToCode();
72947306

@@ -7298,6 +7310,15 @@ public static string ToCode(this object x, IObjectToCode notRecognizedToCode,
72987310
if (x is Type t)
72997311
return t.ToCode(stripNamespace, printType);
73007312

7313+
if (x is Guid guid)
7314+
return "Guid.Parse(" + guid.ToString().ToCode() + ")";
7315+
7316+
if (x is DateTime date)
7317+
return "DateTime.Parse(" + date.ToString().ToCode() + ")";
7318+
7319+
if (x is TimeSpan time)
7320+
return "TimeSpan.Parse(" + time.ToString().ToCode() + ")";
7321+
73017322
var xType = x.GetType();
73027323
var xTypeInfo = xType.GetTypeInfo();
73037324

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using NUnit.Framework;
2+
using System;
3+
using System.Linq.Expressions;
4+
#if LIGHT_EXPRESSION
5+
using static FastExpressionCompiler.LightExpression.Expression;
6+
namespace FastExpressionCompiler.LightExpression.IssueTests
7+
#else
8+
using static System.Linq.Expressions.Expression;
9+
namespace FastExpressionCompiler.IssueTests
10+
#endif
11+
{
12+
[TestFixture]
13+
public class Issue314_LiftToNull_ToExpressionString
14+
{
15+
public int Run()
16+
{
17+
LiftToNull();
18+
CustomMethod();
19+
DateTimeConstant();
20+
NullableDateTimeConstant();
21+
return 4;
22+
}
23+
24+
[Test]
25+
public void LiftToNull()
26+
{
27+
var p = Parameter(typeof(int), "tmp0");
28+
var expr = Expression.MakeBinary(
29+
ExpressionType.Add,
30+
Expression.Constant(1, typeof(int?)),
31+
Expression.Constant(null, typeof(int?)),
32+
liftToNull: true,
33+
null
34+
);
35+
36+
var str = expr.ToExpressionString(out _, out _, out _, true);
37+
Console.WriteLine(str);
38+
Assert.AreEqual(@"
39+
var e = new Expression[2]; // the unique expressions
40+
var expr = MakeBinary(ExpressionType.Add,
41+
e[0]=Constant(1, typeof(int?)),
42+
e[1]=Constant(null, typeof(int?)),
43+
liftToNull: true,
44+
null);
45+
".Trim(), str);
46+
}
47+
48+
[Test]
49+
public void CustomMethod()
50+
{
51+
var x = Parameter(typeof(A), "x");
52+
var expr = Expression.Add(x, x);
53+
var str = expr.ToExpressionString(out _, out _, out _, true);
54+
Console.WriteLine(str);
55+
Assert.AreEqual(@"
56+
var p = new ParameterExpression[1]; // the parameter expressions
57+
var expr = MakeBinary(ExpressionType.Add,
58+
p[0]=Parameter(typeof(Issue314_LiftToNull_ToExpressionString.A), ""x""),
59+
p[0 // (Issue314_LiftToNull_ToExpressionString.A x)
60+
],
61+
liftToNull: false,
62+
typeof(Issue314_LiftToNull_ToExpressionString.A).GetMethods().Single(x => !x.IsGenericMethod && x.Name == ""op_Addition"" && x.GetParameters().Select(y => y.ParameterType).SequenceEqual(new[] { typeof(Issue314_LiftToNull_ToExpressionString.A), typeof(Issue314_LiftToNull_ToExpressionString.A) })));
63+
".Trim(), str);
64+
}
65+
[Test]
66+
public void DateTimeConstant()
67+
{
68+
var expr = Expression.Constant(new DateTime(2020, 3, 13));
69+
var str = expr.ToExpressionString();
70+
Console.WriteLine(str);
71+
Assert.AreEqual(@"
72+
var expr = Constant(DateTime.Parse(""3/13/2020 12:00:00 AM""));
73+
".Trim(), str);
74+
}
75+
[Test]
76+
public void NullableDateTimeConstant()
77+
{
78+
var expr = Expression.Constant(new DateTime(2020, 3, 13), typeof(DateTime?));
79+
var str = expr.ToExpressionString();
80+
Console.WriteLine(str);
81+
Assert.AreEqual(@"
82+
var expr = Constant(DateTime.Parse(""3/13/2020 12:00:00 AM""), typeof(System.DateTime?));
83+
".Trim(), str);
84+
}
85+
86+
class A {
87+
public static A operator +(A x, A y) { return new A(); }
88+
}
89+
}
90+
}

test/FastExpressionCompiler.TestsRunner.Net472/Program.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,14 @@ void Run(Func<int> run, string name = null)
201201
Run(new Issue308_Wrong_delegate_type_returned_with_closure().Run);
202202
Run(new FastExpressionCompiler.LightExpression.IssueTests.Issue308_Wrong_delegate_type_returned_with_closure().Run);
203203

204+
Run(new Issue309_InvalidProgramException_with_MakeBinary_liftToNull_true().Run);
205+
Run(new FastExpressionCompiler.LightExpression.IssueTests.Issue309_InvalidProgramException_with_MakeBinary_liftToNull_true().Run);
206+
204207
Run(new Issue310_InvalidProgramException_ignored_nullable().Run);
205208
Run(new FastExpressionCompiler.LightExpression.IssueTests.Issue310_InvalidProgramException_ignored_nullable().Run);
206209

207-
Run(new Issue309_InvalidProgramException_with_MakeBinary_liftToNull_true().Run);
208-
Run(new FastExpressionCompiler.LightExpression.IssueTests.Issue309_InvalidProgramException_with_MakeBinary_liftToNull_true().Run);
210+
Run(new Issue314_LiftToNull_ToExpressionString().Run);
211+
Run(new FastExpressionCompiler.LightExpression.IssueTests.Issue314_LiftToNull_ToExpressionString().Run);
209212

210213
Console.WriteLine($"============={Environment.NewLine}IssueTests are passing in {sw.ElapsedMilliseconds} ms.");
211214
});

test/FastExpressionCompiler.TestsRunner/Program.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,11 +228,14 @@ void Run(Func<int> run, string name = null)
228228
Run(new Issue308_Wrong_delegate_type_returned_with_closure().Run);
229229
Run(new FastExpressionCompiler.LightExpression.IssueTests.Issue308_Wrong_delegate_type_returned_with_closure().Run);
230230

231+
Run(new Issue309_InvalidProgramException_with_MakeBinary_liftToNull_true().Run);
232+
Run(new FastExpressionCompiler.LightExpression.IssueTests.Issue309_InvalidProgramException_with_MakeBinary_liftToNull_true().Run);
233+
231234
Run(new Issue310_InvalidProgramException_ignored_nullable().Run);
232235
Run(new FastExpressionCompiler.LightExpression.IssueTests.Issue310_InvalidProgramException_ignored_nullable().Run);
233236

234-
Run(new Issue309_InvalidProgramException_with_MakeBinary_liftToNull_true().Run);
235-
Run(new FastExpressionCompiler.LightExpression.IssueTests.Issue309_InvalidProgramException_with_MakeBinary_liftToNull_true().Run);
237+
Run(new Issue314_LiftToNull_ToExpressionString().Run);
238+
Run(new FastExpressionCompiler.LightExpression.IssueTests.Issue314_LiftToNull_ToExpressionString().Run);
236239

237240
Console.WriteLine($"============={Environment.NewLine}IssueTests are passing in {sw.ElapsedMilliseconds} ms.");
238241
});

0 commit comments

Comments
 (0)