Skip to content

Commit

Permalink
FIX #68 Array.Contains not working
Browse files Browse the repository at this point in the history
  • Loading branch information
davideicardi committed May 1, 2018
1 parent e58de49 commit 48abbe4
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 93 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Source code and symbols (.pdb files) for debugging are available on [Symbol Sour
- Can generate delegates or lambda expression
- Full suite of unit tests
- Good performance compared to other similar projects
- Partial support of generic, params array and extension methods
- Partial support of generic, params array and extension methods (only with implicit generic arguments detection)
- Partial support of `dynamic` (`ExpandoObject` for get properties and method invocation, see #72)
- Case insensitive expressions (default is case sensitive)
- Ability to discover identifiers (variables, types, parameters) of a given expression
Expand Down
2 changes: 1 addition & 1 deletion src/DynamicExpresso.Core/DynamicExpresso.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<PackageLicenseUrl>https://github.com/davideicardi/DynamicExpresso#license</PackageLicenseUrl>
<RepositoryUrl>https://github.com/davideicardi/DynamicExpresso.git</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<VersionPrefix>2.0.1</VersionPrefix>
<VersionPrefix>2.0.2</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>

Expand Down
121 changes: 32 additions & 89 deletions src/DynamicExpresso.Core/Parsing/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1262,31 +1262,23 @@ private static void AddInterface(List<Type> types, Type type)
}
}

private class MethodData
{
public MethodBase MethodBase;
public ParameterInfo[] Parameters;
public Expression[] PromotedParameters;
public bool HasParamsArray;
}

private MethodData[] FindBestMethod(IEnumerable<MethodBase> methods, Expression[] args)
private static MethodData[] FindBestMethod(IEnumerable<MethodBase> methods, Expression[] args)
{
var applicable = methods.
Select(m => new MethodData { MethodBase = m, Parameters = m.GetParameters() }).
Where(m => CheckIfMethodIsApplicableAndPrepareIt(m, args)).
ToArray();
if (applicable.Length > 1)
{
applicable = applicable.
return applicable.
Where(m => applicable.All(n => m == n || MethodHasPriority(args, m, n))).
ToArray();
}

return applicable;
}

private bool CheckIfMethodIsApplicableAndPrepareIt(MethodData method, Expression[] args)
private static bool CheckIfMethodIsApplicableAndPrepareIt(MethodData method, Expression[] args)
{
if (method.Parameters.Length > args.Length)
return false;
Expand Down Expand Up @@ -1374,41 +1366,48 @@ private bool CheckIfMethodIsApplicableAndPrepareIt(MethodData method, Expression
{
var methodInfo = (MethodInfo)method.MethodBase;

var genericArgsType = ExtractActualGenericArguments(
method.Parameters.Select(p => p.ParameterType).ToArray(),
method.PromotedParameters.Select(p => p.Type).ToArray());
var actualGenericArgs = ExtractActualGenericArguments(
method.Parameters.Select(p => p.ParameterType).ToArray(),
method.PromotedParameters.Select(p => p.Type).ToArray());

method.MethodBase = methodInfo.MakeGenericMethod(genericArgsType.ToArray());
var genericArgs = methodInfo.GetGenericArguments()
.Select(p => actualGenericArgs[p.Name])
.ToArray();

method.MethodBase = methodInfo.MakeGenericMethod(genericArgs);
}

return true;
}

private List<Type> ExtractActualGenericArguments(Type[] requestedParameters, Type[] actualParameters)
private static Dictionary<string, Type> ExtractActualGenericArguments(
Type[] methodGenericParameters,
Type[] methodActualParameters)
{
var extractedGenericTypes = new List<Type>();
var extractedGenericTypes = new Dictionary<string, Type>();

for (var i = 0; i < requestedParameters.Length; i++)
for (var i = 0; i < methodGenericParameters.Length; i++)
{
var requestedType = requestedParameters[i];
var actualType = actualParameters[i];
var requestedType = methodGenericParameters[i];
var actualType = methodActualParameters[i];

if (requestedType.IsGenericParameter)
{
extractedGenericTypes.Add(actualType);
extractedGenericTypes[requestedType.Name] = actualType;
}
else if (requestedType.ContainsGenericParameters)
{
var innerGenericTypes = ExtractActualGenericArguments(requestedType.GetGenericArguments(), actualType.GetGenericArguments());

extractedGenericTypes.AddRange(innerGenericTypes);
foreach (var innerGenericType in innerGenericTypes)
extractedGenericTypes[innerGenericType.Key] = innerGenericType.Value;
}
}

return extractedGenericTypes;
}

private Expression PromoteExpression(Expression expr, Type type, bool exact)
private static Expression PromoteExpression(Expression expr, Type type, bool exact)
{
if (expr.Type == type) return expr;
if (expr is ConstantExpression)
Expand Down Expand Up @@ -1440,70 +1439,6 @@ private Expression PromoteExpression(Expression expr, Type type, bool exact)
return null;
}

//object ParseNumber(string text, Type type)
//{
// switch (Type.GetTypeCode(GetNonNullableType(type)))
// {
// case TypeCode.SByte:
// sbyte sb;
// if (sbyte.TryParse(text, ParseLiteralNumberStyle, ParseCulture, out sb)) return sb;
// break;
// case TypeCode.Byte:
// byte b;
// if (byte.TryParse(text, ParseLiteralNumberStyle, ParseCulture, out b)) return b;
// break;
// case TypeCode.Int16:
// short s;
// if (short.TryParse(text, ParseLiteralNumberStyle, ParseCulture, out s)) return s;
// break;
// case TypeCode.UInt16:
// ushort us;
// if (ushort.TryParse(text, ParseLiteralUnsignedNumberStyle, ParseCulture, out us)) return us;
// break;
// case TypeCode.Int32:
// int i;
// if (int.TryParse(text, ParseLiteralNumberStyle, ParseCulture, out i)) return i;
// break;
// case TypeCode.UInt32:
// uint ui;
// if (uint.TryParse(text, ParseLiteralUnsignedNumberStyle, ParseCulture, out ui)) return ui;
// break;
// case TypeCode.Int64:
// long l;
// if (long.TryParse(text, ParseLiteralNumberStyle, ParseCulture, out l)) return l;
// break;
// case TypeCode.UInt64:
// ulong ul;
// if (ulong.TryParse(text, ParseLiteralUnsignedNumberStyle, ParseCulture, out ul)) return ul;
// break;
// case TypeCode.Single:
// float f;
// if (float.TryParse(text, ParseLiteralDecimalNumberStyle, ParseCulture, out f)) return f;
// break;
// case TypeCode.Double:
// double d;
// if (double.TryParse(text, ParseLiteralDecimalNumberStyle, ParseCulture, out d)) return d;
// break;
// case TypeCode.Decimal:
// decimal e;
// if (decimal.TryParse(text, ParseLiteralDecimalNumberStyle, ParseCulture, out e)) return e;
// break;
// }
// return null;
//}

//static object ParseEnum(string name, Type type)
//{
// if (type.IsEnum)
// {
// MemberInfo[] memberInfos = type.FindMembers(MemberTypes.Field,
// BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static,
// Type.FilterNameIgnoreCase, name);
// if (memberInfos.Length != 0) return ((FieldInfo)memberInfos[0]).GetValue(null);
// }
// return null;
//}

private static bool IsCompatibleWith(Type source, Type target)
{
if (source == target)
Expand Down Expand Up @@ -2113,7 +2048,7 @@ private void NextToken()
private string GetIdentifier()
{
ValidateToken(TokenId.Identifier, ErrorMessages.IdentifierExpected);
string id = _token.text;
var id = _token.text;
if (id.Length > 1 && id[0] == '@')
id = id.Substring(1);
return id;
Expand All @@ -2139,9 +2074,17 @@ private void ValidateToken(TokenId t)
throw CreateParseException(_token.pos, ErrorMessages.SyntaxError);
}

private Exception CreateParseException(int pos, string format, params object[] args)
private static Exception CreateParseException(int pos, string format, params object[] args)
{
return new ParseException(string.Format(format, args), pos);
}

private class MethodData
{
public MethodBase MethodBase;
public ParameterInfo[] Parameters;
public Expression[] PromotedParameters;
public bool HasParamsArray;
}
}
}
23 changes: 21 additions & 2 deletions test/DynamicExpresso.UnitTest/ExtensionsMethodsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,19 @@ public void Invoke_generic_parameter_extension_method()
Assert.AreEqual(x.GenericParamHello(), target.Eval("x.GenericParamHello()"));
}

[Test]
public void Invoke_generic_parameter_extension_method_with_2_parameters()
{
var x = new MyClass[0];

var target = new Interpreter()
.Reference(typeof(TestExtensionsMethods))
.Reference(typeof(MyClass))
.SetVariable("x", x);

Assert.AreEqual(x.GenericWith2Params(new MyClass()), target.Eval("x.GenericWith2Params(new MyClass())"));
}

[Test]
public void Invoke_generic_with_2_parameters_and_output_extension_method()
{
Expand All @@ -55,7 +68,7 @@ public void Invoke_generic_with_2_parameters_and_output_extension_method()
.Reference(typeof(TestExtensionsMethods))
.SetVariable("x", x);

Assert.AreEqual(x.GenericWith2Params(), target.Eval("x.GenericWith2Params()"));
Assert.AreEqual(x.GenericWith2Args(), target.Eval("x.GenericWith2Args()"));
}

[Test]
Expand Down Expand Up @@ -99,13 +112,19 @@ public static string GenericParamHello<T>(this IEnumerable<T> test)
return "Hello with generic param!";
}

public static string GenericWith2Params<T>(this IEnumerable<T> test, T another)
where T : ExtensionsMethodsTest.MyClass
{
return "Hello with 2 generic param!";
}

public static string GenericMixedParamHello<T>(this IDictionary<string, T> test)
where T : ExtensionsMethodsTest.MyClass
{
return "Hello with generic param!";
}

public static T2 GenericWith2Params<T1, T2>(this IDictionary<T1, T2> test)
public static T2 GenericWith2Args<T1, T2>(this IDictionary<T1, T2> test)
where T2 : ExtensionsMethodsTest.MyClass
{
return test.First().Value;
Expand Down
14 changes: 14 additions & 0 deletions test/DynamicExpresso.UnitTest/GithubIssues.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using NUnit.Framework;
using System.Collections.Generic;
using System.Linq;
// ReSharper disable SpecifyACultureInStringConversionExplicitly

namespace DynamicExpresso.UnitTest
Expand Down Expand Up @@ -28,5 +29,18 @@ public void GitHub_Issue_43()
Assert.AreEqual((.1).ToString(), interpreter.Eval(".1.ToString()"));
Assert.AreEqual((-1-.1-0.1).ToString(), interpreter.Eval("(-1-.1-0.1).ToString()"));
}

[Test]
public void GitHub_Issue_68()
{
var interpreter = new Interpreter();

var array = new[] { 5, 10, 6 };

interpreter.SetVariable("array", array);

Assert.AreEqual(array.Contains(5), interpreter.Eval("array.Contains(5)"));
Assert.AreEqual(array.Contains(3), interpreter.Eval("array.Contains(3)"));
}
}
}

0 comments on commit 48abbe4

Please sign in to comment.