Skip to content

Commit 3f92c1c

Browse files
committed
Updates
1 parent bd46322 commit 3f92c1c

File tree

5 files changed

+278
-54
lines changed

5 files changed

+278
-54
lines changed

src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,11 +1857,8 @@ private Expression ParseMemberAccess(Type? type, Expression? expression, string?
18571857
case 1:
18581858
var method = (MethodInfo)methodBase!;
18591859

1860-
int y = 0;
1861-
y.ToString();
1862-
18631860
if (!PredefinedTypesHelper.IsPredefinedType(_parsingConfig, method.DeclaringType!) &&
1864-
!_predefinedMethodsHelper.IsPredefinedMethod(type!, method))
1861+
!_predefinedMethodsHelper.IsPredefinedMethod(type!, method.DeclaringType!, method))
18651862
{
18661863
throw ParseError(errorPos, Res.MethodIsInaccessible, id, TypeHelper.GetTypeName(method.DeclaringType!));
18671864
}
@@ -1941,7 +1938,7 @@ private bool TryFindPropertyOrField(Type type, string id, Expression? expression
19411938
switch (member)
19421939
{
19431940
case PropertyInfo property:
1944-
var propertyIsStatic = property?.GetGetMethod().IsStatic ?? property?.GetSetMethod().IsStatic ?? false;
1941+
var propertyIsStatic = property.GetGetMethod()?.IsStatic ?? property.GetSetMethod()?.IsStatic ?? false;
19451942
propertyOrFieldExpression = propertyIsStatic ? Expression.Property(null, property) : Expression.Property(expression, property);
19461943
return true;
19471944

src/System.Linq.Dynamic.Core/Parser/PredefinedMethodsHelper.cs

Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,55 +6,53 @@ namespace System.Linq.Dynamic.Core.Parser;
66

77
internal class PredefinedMethodsHelper
88
{
9-
private static readonly BindingFlags _publicInstance = BindingFlags.Public | BindingFlags.Instance;
10-
private static readonly BindingFlags _publicStatic = BindingFlags.Public | BindingFlags.Static;
9+
private const BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance;
10+
private const BindingFlags PublicStatic = BindingFlags.Public | BindingFlags.Static;
1111

12-
internal static readonly MethodInfo ObjectInstanceToString = typeof(object).GetMethod(nameof(ToString), _publicInstance, null, Type.EmptyTypes, null)!;
13-
internal static readonly MethodInfo ObjectInstanceEquals = typeof(object).GetMethod(nameof(Equals), _publicInstance, null, [typeof(object)], null)!;
14-
internal static readonly MethodInfo ObjectStaticEquals = typeof(object).GetMethod(nameof(Equals), _publicStatic, null, [typeof(object), typeof(object)], null)!;
15-
internal static readonly MethodInfo ObjectStaticReferenceEquals = typeof(object).GetMethod(nameof(ReferenceEquals), _publicStatic, null, [typeof(object), typeof(object)], null)!;
12+
internal static readonly MethodInfo ObjectInstanceToString = typeof(object).GetMethod(nameof(ToString), PublicInstance, null, Type.EmptyTypes, null)!;
13+
internal static readonly MethodInfo ObjectInstanceEquals = typeof(object).GetMethod(nameof(Equals), PublicInstance, null, [typeof(object)], null)!;
14+
internal static readonly MethodInfo ObjectStaticEquals = typeof(object).GetMethod(nameof(Equals), PublicStatic, null, [typeof(object), typeof(object)], null)!;
15+
internal static readonly MethodInfo ObjectStaticReferenceEquals = typeof(object).GetMethod(nameof(ReferenceEquals), PublicStatic, null, [typeof(object), typeof(object)], null)!;
1616

1717
private readonly Dictionary<Type, HashSet<MemberInfo>> _supported = new()
1818
{
19-
{ typeof(bool), new HashSet<MemberInfo>() },
20-
{ typeof(char), new HashSet<MemberInfo>() },
21-
{ typeof(string), new HashSet<MemberInfo>() },
22-
{ typeof(sbyte), new HashSet<MemberInfo>() },
23-
{ typeof(byte), new HashSet<MemberInfo>() },
24-
{ typeof(short), new HashSet<MemberInfo>() },
25-
{ typeof(ushort), new HashSet<MemberInfo>() },
26-
{ typeof(int), new HashSet<MemberInfo>() },
27-
{ typeof(uint), new HashSet<MemberInfo>() },
28-
{ typeof(long), new HashSet<MemberInfo>() },
29-
{ typeof(ulong), new HashSet<MemberInfo>() },
30-
{ typeof(float), new HashSet<MemberInfo>() },
31-
{ typeof(double), new HashSet<MemberInfo>() },
32-
{ typeof(decimal), new HashSet<MemberInfo>() },
33-
{ typeof(DateTime), new HashSet<MemberInfo>() },
34-
{ typeof(DateTimeOffset), new HashSet<MemberInfo>() },
35-
{ typeof(TimeSpan), new HashSet<MemberInfo>() },
36-
{ typeof(Guid), new HashSet<MemberInfo>() },
37-
{ typeof(Uri), new HashSet<MemberInfo>() },
38-
{ typeof(Enum), new HashSet<MemberInfo>() },
19+
{ typeof(bool), [] },
20+
{ typeof(byte), [] },
21+
{ typeof(char), [] },
22+
{ typeof(DateTime), [] },
23+
{ typeof(DateTimeOffset), [] },
24+
{ typeof(decimal), [] },
25+
{ typeof(double), [] },
26+
// { typeof(Enum), [] },
27+
{ typeof(float), [] },
28+
{ typeof(Guid), [] },
29+
{ typeof(int), [] },
30+
{ typeof(long), [] },
31+
{ typeof(sbyte), [] },
32+
{ typeof(short), [] },
33+
{ typeof(string), [] },
34+
{ typeof(TimeSpan), [] },
35+
{ typeof(uint), [] },
36+
{ typeof(ulong), [] },
37+
{ typeof(Uri), [] },
38+
{ typeof(ushort), [] },
3939
#if NET6_0_OR_GREATER
40-
{ typeof(DateOnly), new HashSet<MemberInfo>() },
41-
{ typeof(TimeOnly), new HashSet<MemberInfo>() },
40+
{ typeof(DateOnly), [] },
41+
{ typeof(TimeOnly), [] },
4242
#endif
4343
};
4444

4545
public PredefinedMethodsHelper(ParsingConfig config)
4646
{
4747
foreach (var kvp in _supported)
4848
{
49-
TryAdd(kvp.Key, ObjectInstanceEquals);
50-
TryAdd(kvp.Key, kvp.Key.GetMethod(nameof(Equals), _publicInstance, null, [kvp.Key], null));
51-
TryAdd(kvp.Key, kvp.Key.GetMethod(nameof(Equals), _publicInstance, null, [typeof(object)], null));
49+
TryAdd(kvp.Key, kvp.Key.GetMethod(nameof(Equals), PublicInstance, null, [kvp.Key], null));
50+
TryAdd(kvp.Key, kvp.Key.GetMethod(nameof(Equals), PublicInstance, null, [typeof(object)], null));
5251

53-
TryAdd(kvp.Key, ObjectInstanceToString);
54-
TryAdd(kvp.Key, kvp.Key.GetMethod(nameof(ToString), _publicInstance, null, Type.EmptyTypes, null));
55-
TryAdd(kvp.Key, kvp.Key.GetMethod(nameof(ToString), _publicInstance, null, [typeof(string)], null));
56-
TryAdd(kvp.Key, kvp.Key.GetMethod(nameof(ToString), _publicInstance, null, [typeof(IFormatProvider)], null));
57-
TryAdd(kvp.Key, kvp.Key.GetMethod(nameof(ToString), _publicInstance, null, [typeof(string), typeof(IFormatProvider)], null));
52+
TryAdd(kvp.Key, kvp.Key.GetMethod(nameof(ToString), PublicInstance, null, Type.EmptyTypes, null));
53+
TryAdd(kvp.Key, kvp.Key.GetMethod(nameof(ToString), PublicInstance, null, [typeof(string)], null));
54+
TryAdd(kvp.Key, kvp.Key.GetMethod(nameof(ToString), PublicInstance, null, [typeof(IFormatProvider)], null));
55+
TryAdd(kvp.Key, kvp.Key.GetMethod(nameof(ToString), PublicInstance, null, [typeof(string), typeof(IFormatProvider)], null));
5856
}
5957

6058
if (config.AllowEqualsAndToStringMethodsOnObject)
@@ -63,17 +61,28 @@ public PredefinedMethodsHelper(ParsingConfig config)
6361
}
6462
}
6563

66-
public bool IsPredefinedMethod(Type type, MemberInfo member)
64+
public bool IsPredefinedMethod(Type type, Type declaringType, MemberInfo member)
6765
{
6866
Check.NotNull(type);
6967
Check.NotNull(member);
7068

71-
if (!_supported.TryGetValue(type, out var supported) || supported.Count == 0)
69+
if (_supported.TryGetValue(type, out var supportedMethodsForType) && supportedMethodsForType.Count > 0)
7270
{
73-
return false;
71+
return supportedMethodsForType.Contains(member);
7472
}
7573

76-
return supported.Contains(member);
74+
if (_supported.TryGetValue(declaringType, out var supportedMethodsForDeclaringType) && supportedMethodsForDeclaringType.Count > 0)
75+
{
76+
return supportedMethodsForDeclaringType.Contains(member);
77+
}
78+
79+
// Last resort, check if the method name is supported for object
80+
if (_supported.TryGetValue(typeof(object), out var supportedMethodsForObject) && supportedMethodsForObject.Count > 0)
81+
{
82+
return supportedMethodsForObject.Any(x => x.Name == member.Name);
83+
}
84+
85+
return false;
7786
}
7887

7988
private void TryAdd(Type type, MethodInfo? method)

src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,12 @@ public int FindMethod(Type? type, string methodName, bool staticAccess, ref Expr
150150
return 0;
151151
}
152152

153-
public int FindBestMethodBasedOnArguments(IEnumerable<MethodBase> methods, ref Expression[] args, out MethodBase? method)
153+
public int FindBestMethodBasedOnArguments(IEnumerable<MethodBase> methods, ref Expression[] args, out MethodBase? methodOrConstructor)
154154
{
155155
// Passing args by reference is now required with the params array support.
156156
var inlineArgs = args;
157157

158-
MethodData[] applicable = methods
158+
var applicable = methods
159159
.Select(m => new MethodData { MethodBase = m, Parameters = m.GetParameters() })
160160
.Where(m => IsApplicable(m, inlineArgs))
161161
.ToArray();
@@ -175,11 +175,16 @@ public int FindBestMethodBasedOnArguments(IEnumerable<MethodBase> methods, ref E
175175
var methodData = applicable[0];
176176
if (methodData.MethodBase is MethodInfo methodInfo)
177177
{
178-
method = methodInfo.GetBaseDefinition();
178+
// It's a method
179+
var baseMethodInfo = methodInfo.GetBaseDefinition();
180+
181+
// If the declaring type is an object, do not take the object-MethodInfo but keep the original MethodInfo
182+
methodOrConstructor = baseMethodInfo.DeclaringType == typeof(object) ? methodInfo : baseMethodInfo;
179183
}
180184
else
181185
{
182-
method = methodData.MethodBase;
186+
// It's a constructor
187+
methodOrConstructor = methodData.MethodBase;
183188
}
184189

185190
if (args.Length == 0 || args.Length != methodData.Args.Length)
@@ -205,7 +210,7 @@ public int FindBestMethodBasedOnArguments(IEnumerable<MethodBase> methods, ref E
205210
}
206211
else
207212
{
208-
method = null;
213+
methodOrConstructor = null;
209214
}
210215

211216
return applicable.Length;

test/System.Linq.Dynamic.Core.Tests/Parser/MethodFinderTest.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Linq.Dynamic.Core.Parser;
2+
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
23
using System.Linq.Expressions;
34
using Xunit;
45
using static System.Linq.Expressions.Expression;
@@ -30,18 +31,18 @@ public void Method_ToString_OnDynamicLinq_And_SystemLinq_ShouldBeEqual()
3031
}
3132

3233
[Fact]
33-
public void Method_Equals1_OnDynamicLinq_And_SystemLinq_ShouldBeEqual()
34+
public void Method_InstanceEquals_OnDynamicLinq_And_SystemLinq_ShouldBeEqual()
3435
{
3536
// Arrange
3637
var config = new ParsingConfig
3738
{
3839
AllowEqualsAndToStringMethodsOnObject = true
3940
};
4041

41-
Expression<Func<int?, bool>> expr = x => x.Equals("a");
42+
Expression<Func<User, bool>> expr = x => x.Equals("a");
4243

4344
var selector = "Equals(\"a\")";
44-
var prm = Parameter(typeof(int?));
45+
var prm = Parameter(typeof(User));
4546
var parser = new ExpressionParser([prm], selector, [], config);
4647

4748
// Act
@@ -52,7 +53,7 @@ public void Method_Equals1_OnDynamicLinq_And_SystemLinq_ShouldBeEqual()
5253
}
5354

5455
[Fact]
55-
public void Method_Equals2_OnDynamicLinq_And_SystemLinq_ShouldBeEqual()
56+
public void Method_StaticEquals_OnDynamicLinq_And_SystemLinq_ShouldBeEqual()
5657
{
5758
// Arrange
5859
var config = new ParsingConfig

0 commit comments

Comments
 (0)