nreco / lambdaparser

Runtime parser for string expressions (formulas, method calls). Builds dynamic LINQ expression tree and compiles it to lambda delegate.
http://www.nrecosite.com/
MIT License
309 stars 55 forks source link

function call evaluation fails for parameters that don't implement IConvertible #19

Closed lafar6502 closed 5 years ago

lafar6502 commented 5 years ago

Hi, found a problem when evaluating a function call on an externally supplied function - it only works for parameters that implement IConvertible. Simple value types have it but custom classes don't - then the eval fails with InvalidCastException. Test code provided below, stack trace too for the failing case.

What is interesting - when I implement the IConvertible interface it works OK without calling any methods from IConvertible - so it looks like runtime doesn't use it at all, it's required just to be there. But unfortunately i dont have the option to implement IConvertible on all objects passed to evaluator. Can you please verify this and provide some fix? Unfortunately, i'm not able to do the VS builds myself for all .Net versions.

(edit) this is probably caused by always calling Convert.ChangeType on method params in LambdaParameterWrapper which always requires IConvertible. Instead, it should cast if possible, and only do Convert.ChangeType when casting wouldn't work.

Thanks RG

Test Name:  NReco_TestFunctionCall
Test FullName:  Test1.EvalTests.NReco_TestFunctionCall
Test Source:    D:\rafal\facile\FacileWeb\Test1\EvalTests.cs : line 357
Test Outcome:   Failed
Test Duration:  0:00:29.5014222

Result StackTrace:  
at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
   at NReco.Linq.LambdaParameterWrapper.InvokeDelegate(Object obj, Object[] args)
   at lambda_method(Closure , LambdaParameterWrapper , LambdaParameterWrapper )
 --- End of inner exception stack trace ---
    at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Delegate.DynamicInvokeImpl(Object[] args)
   at NReco.Linq.LambdaParser.Eval(String expr, Func`2 getVarValue)
   at NReco.Linq.LambdaParser.Eval(String expr, IDictionary`2 vars)
   at Test1.EvalTests.NReco_TestFunctionCall() in D:\rafal\facile\FacileWeb\Test1\EvalTests.cs:line 371
Result Message: 
Test method Test1.EvalTests.NReco_TestFunctionCall threw exception: 
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidCastException: Object must implement IConvertible.
public class LambdaTestLine
        {
            public int Id { get; set; }
            public decimal Amount { get; set; }
            public string Label { get; set; }
        }

        [TestMethod]
        public void NReco_TestFunctionCall()
        {
            var p = new NReco.Linq.LambdaParser();

            var vars = new Dictionary<string, object>
            {
                {"funfun", (Func<object, string>) (x => "AAA" + x + "BBB") },
                {"someValue", "ding" },
                {"someObject", new LambdaTestLine { Id=150, Amount=22, Label = "xxxx" } }
            };

            var x0 = p.Eval("funfun(someValue)", vars);
            Assert.AreEqual("AAAdingBBB", x0);

            var x1 = p.Eval("funfun(someObject)", vars); //this explodes

        }
VitaliyMF commented 5 years ago

Fixed, shipped in 1.0.10