Skip to content

Commit 3fb84e9

Browse files
authored
Methods should only be callable on predefined types (#669)
* Methods should only be callable on predefined types * add test * ip
1 parent c8edded commit 3fb84e9

File tree

8 files changed

+1737
-1605
lines changed

8 files changed

+1737
-1605
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,36 @@
1-
using JetBrains.Annotations;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using System.Reflection;
43

5-
namespace System.Linq.Dynamic.Core.CustomTypeProviders
4+
namespace System.Linq.Dynamic.Core.CustomTypeProviders;
5+
6+
/// <summary>
7+
/// Interface for providing functionality to find custom types for or resolve any type.
8+
/// </summary>
9+
public interface IDynamicLinqCustomTypeProvider
610
{
711
/// <summary>
8-
/// Interface for providing functionality to find custom types for or resolve any type.
12+
/// Returns a list of custom types that System.Linq.Dynamic.Core will understand.
913
/// </summary>
10-
public interface IDynamicLinqCustomTypeProvider
11-
{
12-
/// <summary>
13-
/// Returns a list of custom types that System.Linq.Dynamic.Core will understand.
14-
/// </summary>
15-
/// <returns>A <see cref="HashSet{Type}" /> list of custom types.</returns>
16-
HashSet<Type> GetCustomTypes();
14+
/// <returns>A <see cref="HashSet{Type}" /> list of custom types.</returns>
15+
HashSet<Type> GetCustomTypes();
1716

18-
/// <summary>
19-
/// Returns a list of custom extension methods that System.Linq.Dynamic.Core will understand.
20-
/// </summary>
21-
/// <returns>A list of custom extension methods that System.Linq.Dynamic.Core will understand.</returns>
22-
Dictionary<Type, List<MethodInfo>> GetExtensionMethods();
17+
/// <summary>
18+
/// Returns a list of custom extension methods that System.Linq.Dynamic.Core will understand.
19+
/// </summary>
20+
/// <returns>A list of custom extension methods that System.Linq.Dynamic.Core will understand.</returns>
21+
Dictionary<Type, List<MethodInfo>> GetExtensionMethods();
2322

24-
/// <summary>
25-
/// Resolve any type by fullname which is registered in the current application domain.
26-
/// </summary>
27-
/// <param name="typeName">The typename to resolve.</param>
28-
/// <returns>A resolved <see cref="Type"/> or null when not found.</returns>
29-
Type? ResolveType(string typeName);
23+
/// <summary>
24+
/// Resolve any type by fullname which is registered in the current application domain.
25+
/// </summary>
26+
/// <param name="typeName">The typename to resolve.</param>
27+
/// <returns>A resolved <see cref="Type"/> or null when not found.</returns>
28+
Type? ResolveType(string typeName);
3029

31-
/// <summary>
32-
/// Resolve any type by the simple name which is registered in the current application domain.
33-
/// </summary>
34-
/// <param name="simpleTypeName">The typename to resolve.</param>
35-
/// <returns>A resolved <see cref="Type"/> or null when not found.</returns>
36-
Type? ResolveTypeBySimpleName(string simpleTypeName);
37-
}
30+
/// <summary>
31+
/// Resolve any type by the simple name which is registered in the current application domain.
32+
/// </summary>
33+
/// <param name="simpleTypeName">The typename to resolve.</param>
34+
/// <returns>A resolved <see cref="Type"/> or null when not found.</returns>
35+
Type? ResolveTypeBySimpleName(string simpleTypeName);
3836
}

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -1696,14 +1696,14 @@ private Expression ParseMemberAccess(Type? type, Expression? expression)
16961696
}
16971697

16981698
Expression[] args = ParseArgumentList();
1699-
switch (_methodFinder.FindMethod(type, id, expression == null, ref expression, ref args, out var mb))
1699+
switch (_methodFinder.FindMethod(type, id, expression == null, ref expression, ref args, out var methodBase))
17001700
{
17011701
case 0:
17021702
throw ParseError(errorPos, Res.NoApplicableMethod, id, TypeHelper.GetTypeName(type));
17031703

17041704
case 1:
1705-
MethodInfo method = (MethodInfo)mb!;
1706-
if (!PredefinedTypesHelper.IsPredefinedType(_parsingConfig, method.DeclaringType!) && !(method.IsPublic && PredefinedTypesHelper.IsPredefinedType(_parsingConfig, method.ReturnType)))
1705+
var method = (MethodInfo)methodBase!;
1706+
if (!PredefinedTypesHelper.IsPredefinedType(_parsingConfig, method.DeclaringType!))
17071707
{
17081708
throw ParseError(errorPos, Res.MethodsAreInaccessible, TypeHelper.GetTypeName(method.DeclaringType!));
17091709
}
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,109 @@
11
using System.Collections.Concurrent;
22
using System.Collections.Generic;
33
using System.Linq.Dynamic.Core.Validation;
4-
using System.Text.RegularExpressions;
54

6-
namespace System.Linq.Dynamic.Core.Parser
5+
namespace System.Linq.Dynamic.Core.Parser;
6+
7+
internal static class PredefinedTypesHelper
78
{
8-
internal static class PredefinedTypesHelper
9-
{
10-
private static readonly string Version = Regex.Match(typeof(PredefinedTypesHelper).AssemblyQualifiedName, @"\d+\.\d+\.\d+\.\d+").ToString();
9+
#if NETSTANDARD2_0
10+
private static readonly string Version = Text.RegularExpressions.Regex.Match(typeof(PredefinedTypesHelper).AssemblyQualifiedName!, @"\d+\.\d+\.\d+\.\d+").ToString();
11+
#endif
1112

12-
// These shorthands have different name than actual type and therefore not recognized by default from the PredefinedTypes.
13-
public static readonly IDictionary<string, Type> PredefinedTypesShorthands = new Dictionary<string, Type>
14-
{
15-
{ "int", typeof(int) },
16-
{ "uint", typeof(uint) },
17-
{ "short", typeof(short) },
18-
{ "ushort", typeof(ushort) },
19-
{ "long", typeof(long) },
20-
{ "ulong", typeof(ulong) },
21-
{ "bool", typeof(bool) },
22-
{ "float", typeof(float) }
23-
};
13+
// These shorthands have different name than actual type and therefore not recognized by default from the PredefinedTypes.
14+
public static readonly IDictionary<string, Type> PredefinedTypesShorthands = new Dictionary<string, Type>
15+
{
16+
{ "int", typeof(int) },
17+
{ "uint", typeof(uint) },
18+
{ "short", typeof(short) },
19+
{ "ushort", typeof(ushort) },
20+
{ "long", typeof(long) },
21+
{ "ulong", typeof(ulong) },
22+
{ "bool", typeof(bool) },
23+
{ "float", typeof(float) }
24+
};
2425

25-
public static readonly IDictionary<Type, int> PredefinedTypes = new ConcurrentDictionary<Type, int>(new Dictionary<Type, int> {
26-
{ typeof(object), 0 },
27-
{ typeof(bool), 0 },
28-
{ typeof(char), 0 },
29-
{ typeof(string), 0 },
30-
{ typeof(sbyte), 0 },
31-
{ typeof(byte), 0 },
32-
{ typeof(short), 0 },
33-
{ typeof(ushort), 0 },
34-
{ typeof(int), 0 },
35-
{ typeof(uint), 0 },
36-
{ typeof(long), 0 },
37-
{ typeof(ulong), 0 },
38-
{ typeof(float), 0 },
39-
{ typeof(double), 0 },
40-
{ typeof(decimal), 0 },
41-
{ typeof(DateTime), 0 },
42-
{ typeof(DateTimeOffset), 0 },
43-
{ typeof(TimeSpan), 0 },
44-
{ typeof(Guid), 0 },
45-
{ typeof(Math), 0 },
46-
{ typeof(Convert), 0 },
47-
{ typeof(Uri), 0 },
26+
public static readonly IDictionary<Type, int> PredefinedTypes = new ConcurrentDictionary<Type, int>(new Dictionary<Type, int> {
27+
{ typeof(object), 0 },
28+
{ typeof(bool), 0 },
29+
{ typeof(char), 0 },
30+
{ typeof(string), 0 },
31+
{ typeof(sbyte), 0 },
32+
{ typeof(byte), 0 },
33+
{ typeof(short), 0 },
34+
{ typeof(ushort), 0 },
35+
{ typeof(int), 0 },
36+
{ typeof(uint), 0 },
37+
{ typeof(long), 0 },
38+
{ typeof(ulong), 0 },
39+
{ typeof(float), 0 },
40+
{ typeof(double), 0 },
41+
{ typeof(decimal), 0 },
42+
{ typeof(DateTime), 0 },
43+
{ typeof(DateTimeOffset), 0 },
44+
{ typeof(TimeSpan), 0 },
45+
{ typeof(Guid), 0 },
46+
{ typeof(Math), 0 },
47+
{ typeof(Convert), 0 },
48+
{ typeof(Uri), 0 },
4849
#if NET6_0_OR_GREATER
49-
{ typeof(DateOnly), 0 },
50-
{ typeof(TimeOnly), 0 }
50+
{ typeof(DateOnly), 0 },
51+
{ typeof(TimeOnly), 0 }
5152
#endif
52-
});
53+
});
5354

54-
static PredefinedTypesHelper()
55-
{
55+
static PredefinedTypesHelper()
56+
{
5657
#if !(NET35 || SILVERLIGHT || NETFX_CORE || WINDOWS_APP || UAP10_0 || NETSTANDARD)
57-
//System.Data.Entity is always here, so overwrite short name of it with EntityFramework if EntityFramework is found.
58-
//EF5(or 4.x??), System.Data.Objects.DataClasses.EdmFunctionAttribute
59-
//There is also an System.Data.Entity, Version=3.5.0.0, but no Functions.
60-
TryAdd("System.Data.Objects.EntityFunctions, System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 1);
61-
TryAdd("System.Data.Objects.SqlClient.SqlFunctions, System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 1);
62-
TryAdd("System.Data.Objects.SqlClient.SqlSpatialFunctions, System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 1);
58+
//System.Data.Entity is always here, so overwrite short name of it with EntityFramework if EntityFramework is found.
59+
//EF5(or 4.x??), System.Data.Objects.DataClasses.EdmFunctionAttribute
60+
//There is also an System.Data.Entity, Version=3.5.0.0, but no Functions.
61+
TryAdd("System.Data.Objects.EntityFunctions, System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 1);
62+
TryAdd("System.Data.Objects.SqlClient.SqlFunctions, System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 1);
63+
TryAdd("System.Data.Objects.SqlClient.SqlSpatialFunctions, System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 1);
6364

64-
//EF6,System.Data.Entity.DbFunctionAttribute
65-
TryAdd("System.Data.Entity.Core.Objects.EntityFunctions, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
66-
TryAdd("System.Data.Entity.DbFunctions, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
67-
TryAdd("System.Data.Entity.Spatial.DbGeography, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
68-
TryAdd("System.Data.Entity.SqlServer.SqlFunctions, EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
69-
TryAdd("System.Data.Entity.SqlServer.SqlSpatialFunctions, EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
65+
//EF6,System.Data.Entity.DbFunctionAttribute
66+
TryAdd("System.Data.Entity.Core.Objects.EntityFunctions, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
67+
TryAdd("System.Data.Entity.DbFunctions, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
68+
TryAdd("System.Data.Entity.Spatial.DbGeography, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
69+
TryAdd("System.Data.Entity.SqlServer.SqlFunctions, EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
70+
TryAdd("System.Data.Entity.SqlServer.SqlSpatialFunctions, EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
7071
#endif
7172

7273
#if NETSTANDARD2_0
73-
TryAdd($"Microsoft.EntityFrameworkCore.DynamicLinq.DynamicFunctions, Microsoft.EntityFrameworkCore.DynamicLinq, Version={Version}, Culture=neutral, PublicKeyToken=974e7e1b462f3693", 3);
74+
TryAdd($"Microsoft.EntityFrameworkCore.DynamicLinq.DynamicFunctions, Microsoft.EntityFrameworkCore.DynamicLinq, Version={Version}, Culture=neutral, PublicKeyToken=974e7e1b462f3693", 3);
7475
#endif
75-
}
76+
}
7677

77-
private static void TryAdd(string typeName, int x)
78+
private static void TryAdd(string typeName, int x)
79+
{
80+
try
7881
{
79-
try
82+
var efType = Type.GetType(typeName);
83+
if (efType != null)
8084
{
81-
Type? efType = Type.GetType(typeName);
82-
if (efType != null)
83-
{
84-
PredefinedTypes.Add(efType, x);
85-
}
86-
}
87-
catch
88-
{
89-
// in case of exception, do not add
85+
PredefinedTypes.Add(efType, x);
9086
}
9187
}
92-
93-
public static bool IsPredefinedType(ParsingConfig config, Type type)
88+
catch
9489
{
95-
Check.NotNull(config, nameof(config));
96-
Check.NotNull(type, nameof(type));
90+
// in case of exception, do not add
91+
}
92+
}
9793

98-
var nonNullableType = TypeHelper.GetNonNullableType(type);
99-
if (PredefinedTypes.ContainsKey(nonNullableType))
100-
{
101-
return true;
102-
}
94+
public static bool IsPredefinedType(ParsingConfig config, Type type)
95+
{
96+
Check.NotNull(config);
97+
Check.NotNull(type);
10398

104-
return config.CustomTypeProvider != null &&
105-
(config.CustomTypeProvider.GetCustomTypes().Contains(type) || config.CustomTypeProvider.GetCustomTypes().Contains(nonNullableType));
99+
var nonNullableType = TypeHelper.GetNonNullableType(type);
100+
if (PredefinedTypes.ContainsKey(nonNullableType))
101+
{
102+
return true;
106103
}
104+
105+
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
106+
return config.CustomTypeProvider != null &&
107+
(config.CustomTypeProvider.GetCustomTypes().Contains(type) || config.CustomTypeProvider.GetCustomTypes().Contains(nonNullableType));
107108
}
108-
}
109+
}

0 commit comments

Comments
 (0)