From b7bef3bb2ee5065808debf63557f3f930ad840de Mon Sep 17 00:00:00 2001 From: glopesdev Date: Mon, 3 Jul 2023 20:50:17 +0100 Subject: [PATCH 1/3] Add regression tests for #1054 --- .../OverloadedCombinatorBuilderTests.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/Bonsai.Core.Tests/OverloadedCombinatorBuilderTests.cs b/Bonsai.Core.Tests/OverloadedCombinatorBuilderTests.cs index 438549f7b..093481366 100644 --- a/Bonsai.Core.Tests/OverloadedCombinatorBuilderTests.cs +++ b/Bonsai.Core.Tests/OverloadedCombinatorBuilderTests.cs @@ -98,6 +98,40 @@ public IObservable Process(IObservable> s } } + class HidingOverloadedCombinatorMock : OverloadedCombinatorMock + { + public new IObservable Process(IObservable source) + { + return source.Select(x => double.NaN); + } + } + + class HidingSpecializedGenericOverloadedCombinatorMock : SpecializedGenericOverloadedCombinatorMock + { + public new IObservable Process(IObservable> source) + { + return source.Select(x => default(TSource)); + } + } + + [Combinator] + class BaseVirtualCombinatorMock + { + public virtual IObservable Process(IObservable source) => source; + } + + class DerivedOverrideCombinatorMock : BaseVirtualCombinatorMock + { + public override IObservable Process(IObservable source) => Observable.Return(string.Empty); + } + + class DerivedOverrideOverloadedCombinatorMock : BaseVirtualCombinatorMock + { + public override IObservable Process(IObservable source) => source; + + public IObservable Process(IObservable _) => Observable.Return(default(object)); + } + [TestMethod] public void Build_DoubleOverloadedMethodCalledWithDouble_ReturnsDoubleValue() { @@ -187,5 +221,49 @@ public void Build_SpecializedGenericOverloadedMethod_ReturnsValue() var result = Last(resultProvider).Result; Assert.AreEqual(value, result); } + + [TestMethod] + public void Build_HidingDoubleOverloadedMethodCalledWithDouble_ReturnsDoubleValue() + { + var value = 5.0; + var combinator = new HidingOverloadedCombinatorMock(); + var source = CreateObservableExpression(Observable.Return(value)); + var resultProvider = TestCombinatorBuilder(combinator, source); + var result = Last(resultProvider).Result; + Assert.AreNotEqual(value, result); + } + + [TestMethod] + public void Build_HidingSpecializedGenericOverloadedMethod_ReturnsValue() + { + var value = 5; + var combinator = new HidingSpecializedGenericOverloadedCombinatorMock(); + var source = CreateObservableExpression(Observable.Return(value).Timestamp()); + var resultProvider = TestCombinatorBuilder(combinator, source); + var result = Last(resultProvider).Result; + Assert.AreNotEqual(value, result); + } + + [TestMethod] + public void Build_DerivedOverrideMethodCalledWithString_ReturnsOverrideValue() + { + var value = "5"; + var combinator = new DerivedOverrideCombinatorMock(); + var source = CreateObservableExpression(Observable.Return(value)); + var resultProvider = TestCombinatorBuilder(combinator, source); + var result = Last(resultProvider).Result; + Assert.AreNotEqual(value, result); + } + + [TestMethod] + public void Build_DerivedOverrideOverloadedMethodCalledWithString_ReturnsObjectValue() + { + var value = "5"; + var combinator = new DerivedOverrideOverloadedCombinatorMock(); + var source = CreateObservableExpression(Observable.Return(value)); + var resultProvider = TestCombinatorBuilder(combinator, source); + var result = Last(resultProvider).Result; + Assert.AreNotEqual(value, result); + } } } From 89c72969e8ed9509f6099983d6bbd8c2705c1440 Mon Sep 17 00:00:00 2001 From: glopesdev Date: Mon, 3 Jul 2023 20:53:11 +0100 Subject: [PATCH 2/3] Prefer applicable methods from most derived type --- Bonsai.Core/Expressions/ExpressionBuilder.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Bonsai.Core/Expressions/ExpressionBuilder.cs b/Bonsai.Core/Expressions/ExpressionBuilder.cs index f0196c4b7..c71691dc4 100644 --- a/Bonsai.Core/Expressions/ExpressionBuilder.cs +++ b/Bonsai.Core/Expressions/ExpressionBuilder.cs @@ -765,6 +765,7 @@ class CallCandidate internal static readonly CallCandidate Ambiguous = new CallCandidate(); internal static readonly CallCandidate None = new CallCandidate(); internal MethodBase method; + internal Type declaringType; internal Expression[] arguments; internal bool generic; internal bool expansion; @@ -818,6 +819,7 @@ static CallCandidate OverloadResolution(IEnumerable methods, params return new CallCandidate { method = method, + declaringType = method.DeclaringType, arguments = callArguments, generic = method.IsGenericMethod, expansion = ParamExpansionRequired(parameters, argumentTypes), @@ -853,6 +855,15 @@ static CallCandidate OverloadResolution(IEnumerable methods, params // skip self-test if (i == j) continue; + // exclude self if declaring type is base type of other; and vice-versa + if (candidates[i].declaringType != candidates[j].declaringType) + { + if (candidates[i].declaringType.IsAssignableFrom(candidates[j].declaringType)) + candidates[i].excluded = true; + else candidates[j].excluded = true; + continue; + } + // compare implicit type conversion var comparison = CompareFunctionMember( candidateParameters[i], From c9221c8a2aab990023ffb8b2a95ca5cd1525761f Mon Sep 17 00:00:00 2001 From: glopesdev Date: Mon, 3 Jul 2023 20:56:04 +0100 Subject: [PATCH 3/3] Use base definition to compare virtual overloads --- Bonsai.Core/Expressions/ExpressionBuilder.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Bonsai.Core/Expressions/ExpressionBuilder.cs b/Bonsai.Core/Expressions/ExpressionBuilder.cs index c71691dc4..126b9974a 100644 --- a/Bonsai.Core/Expressions/ExpressionBuilder.cs +++ b/Bonsai.Core/Expressions/ExpressionBuilder.cs @@ -819,7 +819,9 @@ static CallCandidate OverloadResolution(IEnumerable methods, params return new CallCandidate { method = method, - declaringType = method.DeclaringType, + declaringType = method.IsVirtual + ? ((MethodInfo)method).GetBaseDefinition().DeclaringType + : method.DeclaringType, arguments = callArguments, generic = method.IsGenericMethod, expansion = ParamExpansionRequired(parameters, argumentTypes),