소스 검색

Added Support for Action and Function callbacks

Florian 11 년 전
부모
커밋
0dfa3468fd

+ 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);
         }
 
     }

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

@@ -461,14 +461,65 @@ 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 ShouldUseSystemIO()
         {

+ 38 - 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

+ 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();
+                            }
                         }
                     }