Browse Source

Merge pull request #59 from flts/feature/bindfunction

Feature/bindfunction
Sébastien Ros 11 years ago
parent
commit
af1d2e62da

+ 18 - 0
Jint.Tests/Runtime/Domain/A.cs

@@ -49,6 +49,24 @@ namespace Jint.Tests.Runtime.Domain
         public bool Call7(string str, Func<string, bool> predicate)
         {
             return predicate(str);
+        }
+
+        public string Call8(Func<string> predicate)
+        {
+            return predicate();
+        }
+
+        public void Call9(Action predicate)
+        {
+            predicate();
+        }
+        public void Call10(string str, Action<string> predicate)
+        {
+            predicate(str);
+        }
+        public void Call11(string str, string str2, Action<string, string> predicate)
+        {
+            predicate(str, str2);
         }
 
     }

+ 70 - 0
Jint.Tests/Runtime/InteropTests.cs

@@ -461,14 +461,84 @@ namespace Jint.Tests.Runtime
 
         [Fact]
         public void ShouldExecuteFunctionCallBackAsPredicate()
+        {
+            _engine.SetValue("a", new A());
+            
+            // Func<>
+            RunTest(@"
+                assert(a.Call8(function(){ return 'foo'; }) === 'foo');
+            ");
+        }
+
+        [Fact]
+        public void ShouldExecuteFunctionWithParameterCallBackAsPredicate()
         {
             _engine.SetValue("a", new A());
 
+            // Func<,>
             RunTest(@"
                 assert(a.Call7('foo', function(a){ return a === 'foo'; }) === true);
             ");
         }
 
+        [Fact]
+        public void ShouldExecuteActionCallBackAsPredicate()
+        {
+            _engine.SetValue("a", new A());
+
+            // Action
+            RunTest(@"
+                var value;
+                a.Call9(function(){ value = 'foo'; });
+                assert(value === 'foo');
+            ");
+        }
+
+        [Fact]
+        public void ShouldExecuteActionWithParameterCallBackAsPredicate()
+        {
+            _engine.SetValue("a", new A());
+
+            // Action<>
+            RunTest(@"
+                var value;
+                a.Call10('foo', function(b){ value = b; });
+                assert(value === 'foo');
+            ");
+        }
+
+        [Fact]
+        public void ShouldExecuteActionWithMultipleParametersCallBackAsPredicate()
+        {
+            _engine.SetValue("a", new A());
+
+            // Action<,>
+            RunTest(@"
+                var value;
+                a.Call11('foo', 'bar', function(a,b){ value = a + b; });
+                assert(value === 'foobar');
+            ");
+        }
+
+        [Fact]
+        public void ShouldExecuteActionCallbackOnEventChanged()
+        {
+            var collection = new System.Collections.ObjectModel.ObservableCollection<string>();
+            Assert.True(collection.Count == 0);
+
+            _engine.SetValue("collection", collection);
+
+            RunTest(@"
+                var eventAction;
+                collection.add_CollectionChanged(function(sender, eventArgs) { eventAction = eventArgs.Action; } );
+                collection.Add('test');
+            ");
+
+            var eventAction = _engine.GetValue("eventAction").AsNumber();
+            Assert.True(eventAction == 0);
+            Assert.True(collection.Count == 1);
+        }
+
         [Fact]
         public void ShouldUseSystemIO()
         {

+ 63 - 58
Jint/Runtime/Interop/DefaultTypeConverter.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.ObjectModel;
 using System.Linq;
 using System.Linq.Expressions;
 using Jint.Native;
@@ -43,70 +44,49 @@ namespace Jint.Runtime.Interop
                 {
                     var genericType = type.GetGenericTypeDefinition();
 
-                    if (type == typeof(Func<string, bool>))
-                    {
-                        //return (Func<string, bool>)(x =>
-                        //{
-                        //    var arguments = new JsValue[1];
-                        //    arguments[0] = JsValue.FromObject(_engine, x);
-                        //    var o = function(JsValue.Undefined, arguments).ToObject();
-                        //    return (bool)Convert(o, typeof(bool), formatProvider);
-                        //});
-                    }
-
                     // create the requested Delegate
-                    if (genericType == typeof (Action<>))
-                    {
-                    }
-                    else if (genericType == typeof (Func<>))
+                    if (genericType.Name.StartsWith("Action"))
                     {
+                        var genericArguments = type.GetGenericArguments();
+
+                        var @params = new ParameterExpression[genericArguments.Count()];
+                        for (var i = 0; i < @params.Count(); i++)
+                        {
+                            @params[i] = Expression.Parameter(genericArguments[i], genericArguments[i].Name + i);
+                        }
+                        var @vars = Expression.NewArrayInit(typeof(JsValue), @params.Select(p => Expression.Call(null, typeof(JsValue).GetMethod("FromObject"), Expression.Constant(_engine, typeof(Engine)), p)));
+
+                        var callExpresion = Expression.Block(Expression.Call(
+                                                Expression.Call(Expression.Constant(function.Target),
+                                                    function.Method,
+                                                    Expression.Constant(JsValue.Undefined, typeof(JsValue)),
+                                                    @vars),
+                                                typeof(JsValue).GetMethod("ToObject")), Expression.Empty());
+
+                        return Expression.Lambda(callExpresion, new ReadOnlyCollection<ParameterExpression>(@params));
                     }
-                    else if (genericType == typeof (Func<,>))
+                    else if (genericType.Name.StartsWith("Func"))
                     {
-                        // return a function instance containing the following body
-
-                        //var arguments = new JsValue[1];
-                        //arguments[0] = JsValue.FromObject(_engine, x);
-                        //var o = function(JsValue.Undefined, arguments).ToObject();
-                        //return (bool) Convert(o, typeof (bool), formatProvider);
-
                         var genericArguments = type.GetGenericArguments();
                         var returnType = genericArguments.Last();
-
-                        ParameterExpression xExpr = Expression.Parameter(genericArguments[0], "x");
-
-                        // var arguments = new JsValue[1];
-                        ParameterExpression argumentsVariable = Expression.Variable(typeof (JsValue[]), "arguments");
-                        var argumentsExpression = Expression.Constant(new JsValue[genericArguments.Length - 1]);
-                        Expression.Assign(argumentsVariable, argumentsExpression);
-
-                        //arguments[0] = JsValue.FromObject(_engine, x);
-                        IndexExpression arrayAccess = Expression.ArrayAccess(argumentsExpression, Expression.Constant(0, typeof (int)));
-                        var convertExpression = Expression.Call(null,
-                            typeof (JsValue).GetMethod("FromObject"),
-                            Expression.Constant(_engine, typeof (Engine)),
-                            xExpr);
-
-                        Expression.Assign(arrayAccess, convertExpression);
-
-                        //var o = function(JsValue.Undefined, arguments).ToObject();
-                        ParameterExpression oVariable = Expression.Variable(typeof(object), "o");
-                        Expression.Assign(oVariable,
-                            Expression.Call(Expression.Constant(null),
-                                function.Method, 
-                                Expression.Constant(JsValue.Undefined, typeof (JsValue)),
-                                argumentsExpression));
-
-                        var castExpression = Expression.Convert(
-                            Expression.Call(
-                                Expression.Constant(this), 
-                                this.GetType().GetMethod("Convert"), 
-                                oVariable,
-                                Expression.Constant(returnType), 
-                                Expression.Constant(formatProvider)), 
-                            returnType);
-
-                        return Expression.Lambda(castExpression);
+                        
+                        var @params = new ParameterExpression[genericArguments.Count() - 1];
+                        for (var i = 0; i < @params.Count(); i++)
+                        {
+                            @params[i] = Expression.Parameter(genericArguments[i], genericArguments[i].Name + i);
+                        }
+                        var @vars = Expression.NewArrayInit(typeof(JsValue), @params.Select(p => Expression.Call(null, typeof(JsValue).GetMethod("FromObject"), Expression.Constant(_engine, typeof(Engine)), p)));
+
+                        var callExpresion = Expression.Convert(
+                                                Expression.Call(
+                                                    Expression.Call(Expression.Constant(function.Target),
+                                                            function.Method,
+                                                            Expression.Constant(JsValue.Undefined, typeof(JsValue)),
+                                                            @vars),
+                                                    typeof(JsValue).GetMethod("ToObject")),
+                                                returnType);
+
+                        return Expression.Lambda(callExpresion, new ReadOnlyCollection<ParameterExpression>(@params));
                     }
                 }
                 else
@@ -114,6 +94,31 @@ namespace Jint.Runtime.Interop
                     if (type == typeof (Action))
                     {
                         return (Action)(() => function(JsValue.Undefined, new JsValue[0]));
+                    }
+                    else if (type.IsSubclassOf(typeof(System.MulticastDelegate)))
+                    {
+                        var method = type.GetMethod("Invoke");
+                        var arguments = method.GetParameters();
+
+                        var @params = new ParameterExpression[arguments.Count()];
+                        for (var i = 0; i < @params.Count(); i++)
+                        {
+                            @params[i] = Expression.Parameter(typeof(object), arguments[i].Name);
+                        }
+                        var @vars = Expression.NewArrayInit(typeof(JsValue), @params.Select(p => Expression.Call(null, typeof(JsValue).GetMethod("FromObject"), Expression.Constant(_engine, typeof(Engine)), p)));
+
+                        var callExpression = Expression.Block(
+                                                Expression.Call(
+                                                    Expression.Call(Expression.Constant(function.Target),
+                                                        function.Method,
+                                                        Expression.Constant(JsValue.Undefined, typeof(JsValue)),
+                                                        @vars),
+                                                    typeof(JsValue).GetMethod("ToObject")),
+                                                Expression.Empty());
+
+                        var dynamicExpression = Expression.Invoke(Expression.Lambda(callExpression, new ReadOnlyCollection<ParameterExpression>(@params)), new ReadOnlyCollection<ParameterExpression>(@params));
+
+                        return Expression.Lambda(type, dynamicExpression, new ReadOnlyCollection<ParameterExpression>(@params));
                     }
                 }
 

+ 5 - 0
Jint/Runtime/Interop/MethodInfoFunctionInstance.cs

@@ -45,6 +45,11 @@ namespace Jint.Runtime.Interop
                                 arguments[i].ToObject(),
                                 parameterType,
                                 CultureInfo.InvariantCulture);
+
+                            if (typeof(System.Linq.Expressions.LambdaExpression).IsAssignableFrom(parameters[i].GetType()))
+                            {
+                                parameters[i] = (parameters[i] as System.Linq.Expressions.LambdaExpression).Compile();
+                            }
                         }
                     }