Skip to content

Commit

Permalink
Merge pull request #69 from DavidCizek/master
Browse files Browse the repository at this point in the history
Fix - when method has object parameter and ValueType value is passed into this method, result is exception in System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument.
  • Loading branch information
StefH committed Apr 8, 2017
2 parents b1a05ac + bfcda8e commit a32f832
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 9 deletions.
18 changes: 9 additions & 9 deletions src/System.Linq.Dynamic.Core/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ public Expression Parse(Type resultType, bool createParameterCtor)
int exprPos = _textParser.CurrentToken.Pos;
Expression expr = ParseExpression();
if (resultType != null)
if ((expr = PromoteExpression(expr, resultType, true)) == null)
if ((expr = PromoteExpression(expr, resultType, true, false)) == null)
throw ParseError(exprPos, Res.ExpressionTypeMismatch, GetTypeName(resultType));
_textParser.ValidateToken(TokenId.End, Res.SyntaxError);
return expr;
Expand Down Expand Up @@ -596,11 +596,11 @@ Expression ParseComparison()
if (left.Type != right.Type)
{
Expression e;
if ((e = PromoteExpression(right, left.Type, true)) != null)
if ((e = PromoteExpression(right, left.Type, true, false)) != null)
{
right = e;
}
else if ((e = PromoteExpression(left, right.Type, true)) != null)
else if ((e = PromoteExpression(left, right.Type, true, false)) != null)
{
left = e;
}
Expand Down Expand Up @@ -1067,8 +1067,8 @@ Expression GenerateConditional(Expression test, Expression expr1, Expression exp
throw ParseError(errorPos, Res.FirstExprMustBeBool);
if (expr1.Type != expr2.Type)
{
Expression expr1As2 = expr2 != NullLiteral ? PromoteExpression(expr1, expr2.Type, true) : null;
Expression expr2As1 = expr1 != NullLiteral ? PromoteExpression(expr2, expr1.Type, true) : null;
Expression expr1As2 = expr2 != NullLiteral ? PromoteExpression(expr1, expr2.Type, true, false) : null;
Expression expr2As1 = expr1 != NullLiteral ? PromoteExpression(expr2, expr1.Type, true, false) : null;
if (expr1As2 != null && expr2As1 == null)
{
expr1 = expr1As2;
Expand Down Expand Up @@ -1448,7 +1448,7 @@ Expression ParseElementAccess(Expression expr)
{
if (expr.Type.GetArrayRank() != 1 || args.Length != 1)
throw ParseError(errorPos, Res.CannotIndexMultiDimArray);
Expression index = PromoteExpression(args[0], typeof(int), true);
Expression index = PromoteExpression(args[0], typeof(int), true, false);
if (index == null)
throw ParseError(errorPos, Res.InvalidIndex);
return Expression.ArrayIndex(expr, index);
Expand Down Expand Up @@ -1771,15 +1771,15 @@ bool IsApplicable(MethodData method, Expression[] args)
{
ParameterInfo pi = method.Parameters[i];
if (pi.IsOut) return false;
Expression promoted = PromoteExpression(args[i], pi.ParameterType, false);
Expression promoted = PromoteExpression(args[i], pi.ParameterType, false, method.MethodBase.DeclaringType != typeof(IEnumerableSignatures));
if (promoted == null) return false;
promotedArgs[i] = promoted;
}
method.Args = promotedArgs;
return true;
}

Expression PromoteExpression(Expression expr, Type type, bool exact)
Expression PromoteExpression(Expression expr, Type type, bool exact, bool convertExpr)
{
if (expr.Type == type) return expr;

Expand Down Expand Up @@ -1837,7 +1837,7 @@ Expression PromoteExpression(Expression expr, Type type, bool exact)

if (IsCompatibleWith(expr.Type, type))
{
if (type.GetTypeInfo().IsValueType || exact)
if (type.GetTypeInfo().IsValueType || exact || (expr.Type.GetTypeInfo().IsValueType && convertExpr))
return Expression.Convert(expr, type);

return expr;
Expand Down
75 changes: 75 additions & 0 deletions test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -863,5 +863,80 @@ public void ExpressionTests_Where_DoubleDecimalCompare()

Assert.Equal(expected, result);
}

[Fact]
public void ExpressionTests_MethodCall_ValueTypeToValueTypeParameter()
{
//Arrange
var list = new int[] { 0, 1, 2, 3, 4 };

//Act
var methods = new Methods();
var expectedResult = list.Where(x => methods.Method1(x));
var result = list.AsQueryable().Where("@0.Method1(it)", methods);

//Assert
Assert.Equal(expectedResult.Count(), result.Count());
}

[Fact]
public void ExpressionTests_MethodCall_ValueTypeToObjectParameterWithCast()
{
//Arrange
var list = new int[] { 0, 1, 2, 3, 4 };

//Act
var methods = new Methods();
var expectedResult = list.Where(x => methods.Method2(x));
var result = list.AsQueryable().Where("@0.Method2(object(it))", methods);

//Assert
Assert.Equal(expectedResult.Count(), result.Count());
}

[Fact]
public void ExpressionTests_MethodCall_ValueTypeToObjectParameterWithoutCast()
{
//Arrange
var list = new int[] { 0, 1, 2, 3, 4 };

//Act
var methods = new Methods();
var expectedResult = list.Where(x => methods.Method2(x));
var result = list.AsQueryable().Where("@0.Method2(it)", methods);

//Assert
Assert.Equal(expectedResult.Count(), result.Count());
}

[Fact]
public void ExpressionTests_MethodCall_NullableValueTypeToObjectParameter()
{
//Arrange
var list = new int?[] { 0, 1, 2, 3, 4, null };

//Act
var methods = new Methods();
var expectedResult = list.Where(x => methods.Method2(x));
var result = list.AsQueryable().Where("@0.Method2(it)", methods);

//Assert
Assert.Equal(expectedResult.Count(), result.Count());
}

[Fact]
public void ExpressionTests_MethodCall_ReferenceTypeToObjectParameter()
{
//Arrange
var list = new int[] { 0, 1, 2, 3, 4 }.Select(value => new Methods.Item { Value = value }).ToArray();

//Act
var methods = new Methods();
var expectedResult = list.Where(x => methods.Method3(x));
var result = list.AsQueryable().Where("@0.Method3(it)", methods);

//Assert
Assert.Equal(expectedResult.Count(), result.Count());
}
}
}
26 changes: 26 additions & 0 deletions test/System.Linq.Dynamic.Core.Tests/Helpers/Models/Methods.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace System.Linq.Dynamic.Core.Tests.Helpers.Models
{
public class Methods
{
public bool Method1(int value)
{
return value == 1;
}

public bool Method2(object value)
{
return value != null && (int)value == 1;
}

public bool Method3(object value)
{
Item item = value as Item;
return item != null && item.Value == 1;
}

public class Item
{
public int Value { get; set; }
}
}
}

0 comments on commit a32f832

Please sign in to comment.