From 6f974f0b36693f836d53082272132436b7d6ada6 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Thu, 9 Apr 2026 20:15:30 +0200 Subject: [PATCH] Fix MethodFinder.FirstIsBetterThanSecond --- .../DynamicClassFactory.cs | 24 +++++-- .../Parser/SupportedMethods/MethodFinder.cs | 3 +- .../Parser/ExpressionParserTests.cs | 65 ++++++++++++++++++- 3 files changed, 84 insertions(+), 8 deletions(-) diff --git a/src/System.Linq.Dynamic.Core/DynamicClassFactory.cs b/src/System.Linq.Dynamic.Core/DynamicClassFactory.cs index e84d508d..6c70dcbe 100644 --- a/src/System.Linq.Dynamic.Core/DynamicClassFactory.cs +++ b/src/System.Linq.Dynamic.Core/DynamicClassFactory.cs @@ -296,7 +296,7 @@ private static Type EmitType(IList properties, bool createParam var equalityType = fieldTypeIsAccessible ? fieldType : typeof(object); var equalityComparerT = EqualityComparer.MakeGenericType(equalityType); - // Equals() + // Implement Equals(); MethodInfo equalityComparerTDefault = equalityComparerT.GetMethod("get_Default", BindingFlags.Static | BindingFlags.Public)!; MethodInfo equalityComparerTEquals = equalityComparerT.GetMethod(nameof(EqualityComparer.Equals), BindingFlags.Instance | BindingFlags.Public, null, [equalityType, equalityType], null)!; @@ -306,13 +306,21 @@ private static Type EmitType(IList properties, bool createParam ilgeneratorEquals.Emit(OpCodes.Call, equalityComparerTDefault); ilgeneratorEquals.Emit(OpCodes.Ldarg_0); ilgeneratorEquals.Emit(OpCodes.Ldfld, fieldBuilders[i]); - if (!fieldTypeIsAccessible) ilgeneratorEquals.Emit(OpCodes.Box, fieldType); + if (!fieldTypeIsAccessible) + { + ilgeneratorEquals.Emit(OpCodes.Box, fieldType); + } + ilgeneratorEquals.Emit(OpCodes.Ldloc_0); ilgeneratorEquals.Emit(OpCodes.Ldfld, fieldBuilders[i]); - if (!fieldTypeIsAccessible) ilgeneratorEquals.Emit(OpCodes.Box, fieldType); + if (!fieldTypeIsAccessible) + { + ilgeneratorEquals.Emit(OpCodes.Box, fieldType); + } + ilgeneratorEquals.Emit(OpCodes.Callvirt, equalityComparerTEquals); - // GetHashCode(); + // Implement GetHashCode(); MethodInfo equalityComparerTGetHashCode = equalityComparerT.GetMethod(nameof(EqualityComparer.GetHashCode), BindingFlags.Instance | BindingFlags.Public, null, [equalityType], null)!; ilgeneratorGetHashCode.Emit(OpCodes.Stloc_0); ilgeneratorGetHashCode.Emit(OpCodes.Ldc_I4, -1521134295); @@ -321,11 +329,15 @@ private static Type EmitType(IList properties, bool createParam ilgeneratorGetHashCode.Emit(OpCodes.Call, equalityComparerTDefault); ilgeneratorGetHashCode.Emit(OpCodes.Ldarg_0); ilgeneratorGetHashCode.Emit(OpCodes.Ldfld, fieldBuilders[i]); - if (!fieldTypeIsAccessible) ilgeneratorGetHashCode.Emit(OpCodes.Box, fieldType); + if (!fieldTypeIsAccessible) + { + ilgeneratorGetHashCode.Emit(OpCodes.Box, fieldType); + } + ilgeneratorGetHashCode.Emit(OpCodes.Callvirt, equalityComparerTGetHashCode); ilgeneratorGetHashCode.Emit(OpCodes.Add); - // ToString(); + // Implement ToString(); ilgeneratorToString.Emit(OpCodes.Ldloc_0); ilgeneratorToString.Emit(OpCodes.Ldstr, i == 0 ? $"{{ {fieldName} = " : $", {fieldName} = "); ilgeneratorToString.Emit(OpCodes.Callvirt, StringBuilderAppendString); diff --git a/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs b/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs index 17214eda..41ee4aa8 100644 --- a/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs +++ b/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs @@ -342,7 +342,8 @@ private static bool FirstIsBetterThanSecond(Expression[] args, MethodData first, } var better = false; - for (var i = 0; i < args.Length; i++) + var maxLength = Math.Min(first.Parameters.Length, second.Parameters.Length); + for (var i = 0; i < maxLength; i++) { var result = CompareConversions(args[i].Type, first.Parameters[i].ParameterType, second.Parameters[i].ParameterType); diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.cs index f6696720..8ab3ff09 100644 --- a/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.cs @@ -390,7 +390,7 @@ public void Parse_When_PrioritizePropertyOrFieldOverTheType_IsTrue(string expres CustomTypeProvider = _dynamicTypeProviderMock.Object, AllowEqualsAndToStringMethodsOnObject = true }; - ParameterExpression[] parameters = [ ParameterExpressionHelper.CreateParameterExpression(typeof(Company), "company") ]; + ParameterExpression[] parameters = [ParameterExpressionHelper.CreateParameterExpression(typeof(Company), "company")]; var sut = new ExpressionParser(parameters, expression, null, config); // Act @@ -462,6 +462,69 @@ public void Parse_StringConcat(string expression, string result) parsedExpression.Should().Be(result); } + [Fact] + public void Parse_StringConcat3Strings() + { + // Arrange + var parameters = new[] + { + Expression.Parameter(typeof(string), "x"), + Expression.Parameter(typeof(string), "y") + }; + + var parser = new ExpressionParser( + parameters, + "string.Concat(x, \" - \", y)", + values: null, + parsingConfig: null); + + // Act + var expression = parser.Parse(typeof(string)); + + // Assert + expression.ToString().Should().Be("Concat(x, \" - \", y)"); + + // Compile and invoke + var lambda = Expression.Lambda>(expression, parameters); + var compiled = lambda.Compile(); + var result = compiled("hello", "world"); + + // Assert + result.Should().Be("hello - world"); + } + + [Fact] + public void Parse_StringConcat4Strings() + { + // Arrange + var parameters = new[] + { + Expression.Parameter(typeof(string), "x"), + Expression.Parameter(typeof(string), "y"), + Expression.Parameter(typeof(string), "z") + }; + + var parser = new ExpressionParser( + parameters, + "string.Concat(x, \" - \", y, z)", + values: null, + parsingConfig: null); + + // Act + var expression = parser.Parse(typeof(string)); + + // Assert + expression.ToString().Should().Be("Concat(x, \" - \", y, z)"); + + // Compile and invoke + var lambda = Expression.Lambda>(expression, parameters); + var compiled = lambda.Compile(); + var result = compiled("hello", "earth", "moon"); + + // Assert + result.Should().Be("hello - earthmoon"); + } + [Fact] public void Parse_InvalidExpressionShouldThrowArgumentException() {