Skip to content

Commit

Permalink
Methods should only be callable on predefined types (#669)
Browse files Browse the repository at this point in the history
* Methods should only be callable on predefined types

* add test

* ip
  • Loading branch information
StefH committed Feb 14, 2023
1 parent c8edded commit 3fb84e9
Show file tree
Hide file tree
Showing 8 changed files with 1,737 additions and 1,605 deletions.
Original file line number Diff line number Diff line change
@@ -1,38 +1,36 @@
using JetBrains.Annotations;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Reflection;

namespace System.Linq.Dynamic.Core.CustomTypeProviders
namespace System.Linq.Dynamic.Core.CustomTypeProviders;

/// <summary>
/// Interface for providing functionality to find custom types for or resolve any type.
/// </summary>
public interface IDynamicLinqCustomTypeProvider
{
/// <summary>
/// Interface for providing functionality to find custom types for or resolve any type.
/// Returns a list of custom types that System.Linq.Dynamic.Core will understand.
/// </summary>
public interface IDynamicLinqCustomTypeProvider
{
/// <summary>
/// Returns a list of custom types that System.Linq.Dynamic.Core will understand.
/// </summary>
/// <returns>A <see cref="HashSet{Type}" /> list of custom types.</returns>
HashSet<Type> GetCustomTypes();
/// <returns>A <see cref="HashSet{Type}" /> list of custom types.</returns>
HashSet<Type> GetCustomTypes();

/// <summary>
/// Returns a list of custom extension methods that System.Linq.Dynamic.Core will understand.
/// </summary>
/// <returns>A list of custom extension methods that System.Linq.Dynamic.Core will understand.</returns>
Dictionary<Type, List<MethodInfo>> GetExtensionMethods();
/// <summary>
/// Returns a list of custom extension methods that System.Linq.Dynamic.Core will understand.
/// </summary>
/// <returns>A list of custom extension methods that System.Linq.Dynamic.Core will understand.</returns>
Dictionary<Type, List<MethodInfo>> GetExtensionMethods();

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

/// <summary>
/// Resolve any type by the simple name which is registered in the current application domain.
/// </summary>
/// <param name="simpleTypeName">The typename to resolve.</param>
/// <returns>A resolved <see cref="Type"/> or null when not found.</returns>
Type? ResolveTypeBySimpleName(string simpleTypeName);
}
/// <summary>
/// Resolve any type by the simple name which is registered in the current application domain.
/// </summary>
/// <param name="simpleTypeName">The typename to resolve.</param>
/// <returns>A resolved <see cref="Type"/> or null when not found.</returns>
Type? ResolveTypeBySimpleName(string simpleTypeName);
}
6 changes: 3 additions & 3 deletions src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1696,14 +1696,14 @@ private Expression ParseMemberAccess(Type? type, Expression? expression)
}

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

case 1:
MethodInfo method = (MethodInfo)mb!;
if (!PredefinedTypesHelper.IsPredefinedType(_parsingConfig, method.DeclaringType!) && !(method.IsPublic && PredefinedTypesHelper.IsPredefinedType(_parsingConfig, method.ReturnType)))
var method = (MethodInfo)methodBase!;
if (!PredefinedTypesHelper.IsPredefinedType(_parsingConfig, method.DeclaringType!))
{
throw ParseError(errorPos, Res.MethodsAreInaccessible, TypeHelper.GetTypeName(method.DeclaringType!));
}
Expand Down
165 changes: 83 additions & 82 deletions src/System.Linq.Dynamic.Core/Parser/PredefinedTypesHelper.cs
Original file line number Diff line number Diff line change
@@ -1,108 +1,109 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq.Dynamic.Core.Validation;
using System.Text.RegularExpressions;

namespace System.Linq.Dynamic.Core.Parser
namespace System.Linq.Dynamic.Core.Parser;

internal static class PredefinedTypesHelper
{
internal static class PredefinedTypesHelper
{
private static readonly string Version = Regex.Match(typeof(PredefinedTypesHelper).AssemblyQualifiedName, @"\d+\.\d+\.\d+\.\d+").ToString();
#if NETSTANDARD2_0
private static readonly string Version = Text.RegularExpressions.Regex.Match(typeof(PredefinedTypesHelper).AssemblyQualifiedName!, @"\d+\.\d+\.\d+\.\d+").ToString();
#endif

// These shorthands have different name than actual type and therefore not recognized by default from the PredefinedTypes.
public static readonly IDictionary<string, Type> PredefinedTypesShorthands = new Dictionary<string, Type>
{
{ "int", typeof(int) },
{ "uint", typeof(uint) },
{ "short", typeof(short) },
{ "ushort", typeof(ushort) },
{ "long", typeof(long) },
{ "ulong", typeof(ulong) },
{ "bool", typeof(bool) },
{ "float", typeof(float) }
};
// These shorthands have different name than actual type and therefore not recognized by default from the PredefinedTypes.
public static readonly IDictionary<string, Type> PredefinedTypesShorthands = new Dictionary<string, Type>
{
{ "int", typeof(int) },
{ "uint", typeof(uint) },
{ "short", typeof(short) },
{ "ushort", typeof(ushort) },
{ "long", typeof(long) },
{ "ulong", typeof(ulong) },
{ "bool", typeof(bool) },
{ "float", typeof(float) }
};

public static readonly IDictionary<Type, int> PredefinedTypes = new ConcurrentDictionary<Type, int>(new Dictionary<Type, int> {
{ typeof(object), 0 },
{ typeof(bool), 0 },
{ typeof(char), 0 },
{ typeof(string), 0 },
{ typeof(sbyte), 0 },
{ typeof(byte), 0 },
{ typeof(short), 0 },
{ typeof(ushort), 0 },
{ typeof(int), 0 },
{ typeof(uint), 0 },
{ typeof(long), 0 },
{ typeof(ulong), 0 },
{ typeof(float), 0 },
{ typeof(double), 0 },
{ typeof(decimal), 0 },
{ typeof(DateTime), 0 },
{ typeof(DateTimeOffset), 0 },
{ typeof(TimeSpan), 0 },
{ typeof(Guid), 0 },
{ typeof(Math), 0 },
{ typeof(Convert), 0 },
{ typeof(Uri), 0 },
public static readonly IDictionary<Type, int> PredefinedTypes = new ConcurrentDictionary<Type, int>(new Dictionary<Type, int> {
{ typeof(object), 0 },
{ typeof(bool), 0 },
{ typeof(char), 0 },
{ typeof(string), 0 },
{ typeof(sbyte), 0 },
{ typeof(byte), 0 },
{ typeof(short), 0 },
{ typeof(ushort), 0 },
{ typeof(int), 0 },
{ typeof(uint), 0 },
{ typeof(long), 0 },
{ typeof(ulong), 0 },
{ typeof(float), 0 },
{ typeof(double), 0 },
{ typeof(decimal), 0 },
{ typeof(DateTime), 0 },
{ typeof(DateTimeOffset), 0 },
{ typeof(TimeSpan), 0 },
{ typeof(Guid), 0 },
{ typeof(Math), 0 },
{ typeof(Convert), 0 },
{ typeof(Uri), 0 },
#if NET6_0_OR_GREATER
{ typeof(DateOnly), 0 },
{ typeof(TimeOnly), 0 }
{ typeof(DateOnly), 0 },
{ typeof(TimeOnly), 0 }
#endif
});
});

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

//EF6,System.Data.Entity.DbFunctionAttribute
TryAdd("System.Data.Entity.Core.Objects.EntityFunctions, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
TryAdd("System.Data.Entity.DbFunctions, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
TryAdd("System.Data.Entity.Spatial.DbGeography, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
TryAdd("System.Data.Entity.SqlServer.SqlFunctions, EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
TryAdd("System.Data.Entity.SqlServer.SqlSpatialFunctions, EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
//EF6,System.Data.Entity.DbFunctionAttribute
TryAdd("System.Data.Entity.Core.Objects.EntityFunctions, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
TryAdd("System.Data.Entity.DbFunctions, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
TryAdd("System.Data.Entity.Spatial.DbGeography, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
TryAdd("System.Data.Entity.SqlServer.SqlFunctions, EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
TryAdd("System.Data.Entity.SqlServer.SqlSpatialFunctions, EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", 2);
#endif

#if NETSTANDARD2_0
TryAdd($"Microsoft.EntityFrameworkCore.DynamicLinq.DynamicFunctions, Microsoft.EntityFrameworkCore.DynamicLinq, Version={Version}, Culture=neutral, PublicKeyToken=974e7e1b462f3693", 3);
TryAdd($"Microsoft.EntityFrameworkCore.DynamicLinq.DynamicFunctions, Microsoft.EntityFrameworkCore.DynamicLinq, Version={Version}, Culture=neutral, PublicKeyToken=974e7e1b462f3693", 3);
#endif
}
}

private static void TryAdd(string typeName, int x)
private static void TryAdd(string typeName, int x)
{
try
{
try
var efType = Type.GetType(typeName);
if (efType != null)
{
Type? efType = Type.GetType(typeName);
if (efType != null)
{
PredefinedTypes.Add(efType, x);
}
}
catch
{
// in case of exception, do not add
PredefinedTypes.Add(efType, x);
}
}

public static bool IsPredefinedType(ParsingConfig config, Type type)
catch
{
Check.NotNull(config, nameof(config));
Check.NotNull(type, nameof(type));
// in case of exception, do not add
}
}

var nonNullableType = TypeHelper.GetNonNullableType(type);
if (PredefinedTypes.ContainsKey(nonNullableType))
{
return true;
}
public static bool IsPredefinedType(ParsingConfig config, Type type)
{
Check.NotNull(config);
Check.NotNull(type);

return config.CustomTypeProvider != null &&
(config.CustomTypeProvider.GetCustomTypes().Contains(type) || config.CustomTypeProvider.GetCustomTypes().Contains(nonNullableType));
var nonNullableType = TypeHelper.GetNonNullableType(type);
if (PredefinedTypes.ContainsKey(nonNullableType))
{
return true;
}

// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
return config.CustomTypeProvider != null &&
(config.CustomTypeProvider.GetCustomTypes().Contains(type) || config.CustomTypeProvider.GetCustomTypes().Contains(nonNullableType));
}
}
}
Loading

0 comments on commit 3fb84e9

Please sign in to comment.