diff --git a/src/Bonsai.Scripting.Expressions.Tests/ExpressionScriptingTests.cs b/src/Bonsai.Scripting.Expressions.Tests/ExpressionScriptingTests.cs index 2d41330..6a8abfd 100644 --- a/src/Bonsai.Scripting.Expressions.Tests/ExpressionScriptingTests.cs +++ b/src/Bonsai.Scripting.Expressions.Tests/ExpressionScriptingTests.cs @@ -70,6 +70,23 @@ public Task TestCasingCompatibility(string expression, TSource return AssertExpressionTransform(expression, value, expected); } + [TestMethod] + [DataRow("it.Value == NestedEnum.B", true)] + [DataRow("NestedEnum(it.Value + 1)", NestedEnum.C)] + public Task TestNestedPropertyType(string expression, TResult expected) + { + return AssertExpressionTransform(expression, new NestedElement { Value = NestedEnum.B }, expected); + } + + [TestMethod] + [DataRow("it.Element.Value == NestedEnum.B", true)] + [DataRow("NestedElement(object(it.Element)).Value", NestedEnum.B)] + public Task TestTransitiveNestedPropertyType(string expression, TResult expected) + { + var value = new NestedContainer { Element = new NestedElement { Value = NestedEnum.B } }; + return AssertExpressionTransform(expression, value, expected); + } + [TestMethod] [DataRow("")] [DataRow("string(it)")] @@ -79,6 +96,18 @@ public Task TestInvalidExpression(string expression) AssertExpressionTransform(expression, 42, (object)null)); } + public enum NestedEnum { A, B, C } + + public class NestedElement + { + public NestedEnum Value { get; set; } + } + + public class NestedContainer + { + public NestedElement Element { get; set; } + } + class Return(TValue value) : Source { public TValue Value { get; } = value; diff --git a/src/Bonsai.Scripting.Expressions/ParsingConfigHelper.cs b/src/Bonsai.Scripting.Expressions/ParsingConfigHelper.cs index 0929d2e..5dbbecb 100644 --- a/src/Bonsai.Scripting.Expressions/ParsingConfigHelper.cs +++ b/src/Bonsai.Scripting.Expressions/ParsingConfigHelper.cs @@ -24,16 +24,25 @@ static IDynamicLinqCustomTypeProvider CreateCustomTypeProvider(ParsingConfig con static IEnumerable EnumerateTypeHierarchy(Type type) { - var interfaces = type.GetInterfaces(); - for (int i = 0; i < interfaces.Length; i++) + var visited = new HashSet(); + var stack = new Stack(); + stack.Push(type); + while (stack.Count > 0) { - yield return interfaces[i]; - } - - while (type is not null) - { - yield return type; - type = type.BaseType; + var current = stack.Pop(); + if (!visited.Add(current)) continue; + foreach (var interfaceType in current.GetInterfaces()) + { + yield return interfaceType; + } + for (var baseType = current; baseType is not null; baseType = baseType.BaseType) + { + yield return baseType; + } + foreach (var property in current.GetProperties()) + { + stack.Push(property.PropertyType); + } } }