dynamicexpresso / DynamicExpresso

C# expressions interpreter
http://dynamic-expresso.azurewebsites.net/
MIT License
2.02k stars 379 forks source link

ArgumentException when calling Count() method from object returned by dynamic #324

Closed alser closed 1 week ago

alser commented 2 weeks ago

Issue is reproduced in following default console template:

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
    </PropertyGroup>

    <ItemGroup>
      <PackageReference Include="DynamicExpresso.Core" Version="2.17.1" />
    </ItemGroup>

</Project>
using System.Dynamic;
using DynamicExpresso;

var interpreter = new Interpreter();
var expr = interpreter.Parse("p.Value.Count()", new Parameter("p", typeof(SampleParameter)));
var result = expr.Invoke(new SampleParameter());

Console.WriteLine($"Result: {result}");

public sealed class SampleClass
{
    public int Count() => 42;
}

public sealed class SampleParameter : DynamicObject
{
    public override bool TryGetMember(GetMemberBinder binder, out object? result)
    {
        switch (binder.Name)
        {
            case "Value":
                result = new SampleClass();
                return true;

            default:
                result = null;
                return false;
        }
    }
}

Here we have parameter SampleParameter which is a dynamic object. Its dynamic property Value returns some other non-dynamic object SampleClass, which in turn has method named Count().

Trying to parse expression like "p.Value.Count()" results in exception:

System.ArgumentException: Type System.Collections.Generic.IEnumerable`1[TSource] contains generic parameters (Parameter 'type')
   at System.Dynamic.Utils.TypeUtils.ValidateType(Type type, String paramName, Int32 index)
   at System.Dynamic.Utils.TypeUtils.ValidateType(Type type, String paramName, Boolean allowByRef, Boolean allowPointer)
   at System.Linq.Expressions.Expression.Convert(Expression expression, Type type, MethodInfo method)
   at DynamicExpresso.Resolution.ExpressionUtils.PromoteExpression(Expression expr, Type type, Boolean exact)
   at DynamicExpresso.Resolution.MethodResolution.CheckIfMethodIsApplicableAndPrepareIt(MethodData method, Expression[] args)
   at DynamicExpresso.Resolution.MethodResolution.<>c__DisplayClass1_0.<FindBestMethod>b__0(MethodData m)
   at System.Linq.Enumerable.WhereEnumerableIterator`1.ToArray()
   at DynamicExpresso.Resolution.MethodResolution.FindBestMethod(IEnumerable`1 methods, Expression[] args)
   at DynamicExpresso.Resolution.MethodResolution.FindBestMethod(IEnumerable`1 methods, Expression[] args)
   at DynamicExpresso.Reflection.MemberFinder.FindExtensionMethods(String methodName, Expression[] args)
   at DynamicExpresso.Parsing.Parser.ParseExtensionMethodInvocation(Type type, Expression instance, Int32 errorPos, String id, Expression[] args)
   at DynamicExpresso.Parsing.Parser.ParseMethodInvocation(Type type, Expression instance, Int32 errorPos, String methodName, TokenId open, String openExpected, TokenId close, String closeExpected)
   at DynamicExpresso.Parsing.Parser.ParseMethodInvocation(Type type, Expression instance, Int32 errorPos, String methodName)
   at DynamicExpresso.Parsing.Parser.ParseMemberAccess(Type type, Expression instance)
   at DynamicExpresso.Parsing.Parser.ParseMemberAccess(Expression instance)
   at DynamicExpresso.Parsing.Parser.ParsePrimary()
   at DynamicExpresso.Parsing.Parser.ParseUnary()
   at DynamicExpresso.Parsing.Parser.ParseMultiplicative()
   at DynamicExpresso.Parsing.Parser.ParseAdditive()
   at DynamicExpresso.Parsing.Parser.ParseShift()
   at DynamicExpresso.Parsing.Parser.ParseTypeTesting()
   at DynamicExpresso.Parsing.Parser.ParseComparison()
   at DynamicExpresso.Parsing.Parser.ParseLogicalAnd()
   at DynamicExpresso.Parsing.Parser.ParseLogicalXor()
   at DynamicExpresso.Parsing.Parser.ParseLogicalOr()
   at DynamicExpresso.Parsing.Parser.ParseConditionalAnd()
   at DynamicExpresso.Parsing.Parser.ParseConditionalOr()
   at DynamicExpresso.Parsing.Parser.ParseConditional()
   at DynamicExpresso.Parsing.Parser.ParseAssignment()
   at DynamicExpresso.Parsing.Parser.ParseExpressionSegment()
   at DynamicExpresso.Parsing.Parser.ParseExpressionSegment(Type returnType)
   at DynamicExpresso.Parsing.Parser.Parse()
   at DynamicExpresso.Parsing.Parser.Parse(ParserArguments arguments)
   at DynamicExpresso.Interpreter.ParseAsLambda(String expressionText, Type expressionType, Parameter[] parameters)
   at DynamicExpresso.Interpreter.Parse(String expressionText, Type expressionType, Parameter[] parameters)
   at DynamicExpresso.Interpreter.Parse(String expressionText, Parameter[] parameters)
   at Program.<Main>$(String[] args) in C:\Users\User\RiderProjects\ConsoleApp2\ConsoleApp2\Program.cs:line 5
  1. If method Count() renamed to something else (like Count2()), then all is ok, expression p.Value.Count2() successfully returns our magic number.
  2. Using DynamicExpresso.Core 2.16.1 no issues occur with Count() method.

It may have something to do with commit 2696232a8dbe1c52371d0a59602e6c9e60ddd781 where condition "expr is DynamicExpression" is added.

davideicardi commented 2 weeks ago

Thank you for the bug report. I will try to fix it in the future. As usual any help is appreciated.

davideicardi commented 1 week ago

It could be related to #325 .

metoule commented 1 week ago

Yes, the root cause is the same as #325. I'll open a PR to revert the change made in #296

metoule commented 1 week ago

Closing this issue as duplicate of #325