Skip to content

Commit

Permalink
Merge pull request #1120 from riganti/fix/indexing-expression-evaluat…
Browse files Browse the repository at this point in the history
…ed-to-null

Fixed issue with indexing property evaluated to `null`
  • Loading branch information
quigamdev committed Aug 27, 2021
2 parents 96e2611 + 4ebe7d0 commit d98f33c
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/DotVVM.Framework.Tests/Binding/BindingCompilationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,7 @@ class TestViewModel
{
public string StringProp { get; set; }
public int IntProp { get; set; }
public int? NullableIntProp { get; set; }
public double DoubleProp { get; set; }
public TestViewModel2 TestViewModel2 { get; set; }
public TestViewModel2 TestViewModel2B { get; set; }
Expand All @@ -979,6 +980,8 @@ class TestViewModel
public long[] LongArray => new long[] { 1, 2, long.MaxValue };
public List<long> LongList => new List<long>() { 1, 2, long.MaxValue };
public string[] StringArray => new string[] { "Hello ", "DotVVM" };
public Dictionary<string, TestViewModel2> StringVmDictionary = new() { { "a", new TestViewModel2() }, { "b", new TestViewModel2() } };
public Dictionary<int?, TestViewModel2> NullableIntVmDictionary = new() { { 0, new TestViewModel2() }, { 1, new TestViewModel2() } };
public TestViewModel2[] VmArray => new TestViewModel2[] { new TestViewModel2() };
public int[] IntArray { get; set; }

Expand Down
9 changes: 9 additions & 0 deletions src/DotVVM.Framework.Tests/Binding/NullPropagationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,15 @@ public void Indexer()
Assert.IsNull(EvalExpression<TestViewModel>(v => v.TestViewModel2.Collection[0].StringValue.Length + 5, new TestViewModel { IntArray = null }));
}

[TestMethod]
public void IndexerArgument()
{
Assert.IsNull(EvalExpression<TestViewModel>(v => v.IntArray[v.NullableIntProp.Value], new TestViewModel()));
Assert.IsNull(EvalExpression<TestViewModel>(v => v.TestViewModel2.Collection[v.NullableIntProp.Value], new TestViewModel()));
Assert.IsNull(EvalExpression<TestViewModel>(v => v.StringVmDictionary[v.StringProp], new TestViewModel()));
Assert.IsNull(EvalExpression<TestViewModel>(v => v.NullableIntVmDictionary[v.NullableIntProp], new TestViewModel()));
}

[TestMethod]
public void Coalesce()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,48 @@ protected override Expression VisitIndex(IndexExpression node)

protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.Attributes.HasFlag(MethodAttributes.SpecialName))
{
var expr = TryVisitMethodCallWithSpecialName(node);
if (expr != null)
return expr;
}

return CheckForNull(Visit(node.Object), target =>
Expression.Call(target, node.Method, UnwrapNullableTypes(node.Arguments)),
suppress: node.Object?.Type?.IsNullable() ?? true
);
}

private Expression TryVisitMethodCallWithSpecialName(MethodCallExpression node)
{
// Check if we are translating an access to indexer property
if (node.Method.Name.StartsWith("get_", StringComparison.Ordinal) || node.Method.Name.StartsWith("set_", StringComparison.Ordinal))
{
var targetType = node.Object?.Type;
if (targetType == null)
return null;

var indexer = targetType.GetProperties().SingleOrDefault(p => p.GetIndexParameters().Length > 0);
if (indexer == null)
return null;

if (!node.Method.Name.Equals($"get_{indexer.Name}", StringComparison.Ordinal) && !node.Method.Name.Equals($"set_{indexer.Name}", StringComparison.Ordinal))
return null;

return CheckForNull(Visit(node.Object), target =>
{
return CheckForNull(Visit(node.Arguments.First()), index =>
{
var convertedIndex = TypeConversion.ImplicitConversion(index, node.Method.GetParameters().First().ParameterType);
return Expression.Call(target, node.Method, new[] { convertedIndex }.Concat(node.Arguments.Skip(1)));
});
}, suppress: node.Object?.Type?.IsNullable() ?? true);
}

return null;
}

protected override Expression VisitNew(NewExpression node)
{
return Expression.New(node.Constructor, UnwrapNullableTypes(node.Arguments));
Expand Down

0 comments on commit d98f33c

Please sign in to comment.