Procházet zdrojové kódy

Improve function interop (#1074)

* script functions should resolve to same reference
* fix construct crashing without context
Gökhan Kurt před 3 roky
rodič
revize
95f25aaad6

+ 67 - 0
Jint.Tests/Runtime/FunctionTests.cs

@@ -158,5 +158,72 @@ assertEqual(booleanCount, 1);
             ev(null, new JsValue[] { 20 });
             Assert.Equal(30, engine.Evaluate("a"));
         }
+
+
+        [Fact]
+        public void BoundFunctionsCanBePassedToHost()
+        {
+            var engine = new Engine();
+            Func<JsValue, JsValue[], JsValue> ev = null;
+
+            void addListener(Func<JsValue, JsValue[], JsValue> callback)
+            {
+                ev = callback;
+            }
+
+            engine.SetValue("addListener", new Action<Func<JsValue, JsValue[], JsValue>>(addListener));
+
+            engine.Execute(@"
+                var a = 5;
+
+                (function() {
+                    addListener(function (acc, val) {
+                        a = (val || 0) + acc;
+                    }.bind(null, 10));
+                })();
+            ");
+
+            Assert.Equal(5, engine.Evaluate("a"));
+
+            ev(null, new JsValue[0]);
+            Assert.Equal(10, engine.Evaluate("a"));
+
+            ev(null, new JsValue[] { 20 });
+            Assert.Equal(30, engine.Evaluate("a"));
+        }
+
+        [Fact]
+        public void ConstructorsCanBePassedToHost()
+        {
+            var engine = new Engine();
+            Func<JsValue, JsValue[], JsValue> ev = null;
+
+            void addListener(Func<JsValue, JsValue[], JsValue> callback)
+            {
+                ev = callback;
+            }
+
+            engine.SetValue("addListener", new Action<Func<JsValue, JsValue[], JsValue>>(addListener));
+
+            engine.Execute(@"addListener(Boolean)");
+
+            Assert.Equal(true, ev(JsValue.Undefined, new JsValue[] { "test" }));
+            Assert.Equal(true, ev(JsValue.Undefined, new JsValue[] { 5 }));
+            Assert.Equal(false, ev(JsValue.Undefined, new JsValue[] { false }));
+            Assert.Equal(false, ev(JsValue.Undefined, new JsValue[] { 0}));
+            Assert.Equal(false, ev(JsValue.Undefined, new JsValue[] { JsValue.Undefined }));
+        }
+
+
+        [Fact]
+        public void FunctionsShouldResolveToSameReference()
+        {
+            var engine = new Engine();
+            engine.SetValue("equal", new Action<object, object>(Assert.Equal));
+            engine.Execute(@"
+                function testFn() {}
+                equal(testFn, testFn);
+            ");
+        }
     }
 }

+ 1 - 1
Jint/Native/Function/FunctionInstance.cs

@@ -307,7 +307,7 @@ namespace Jint.Native.Function
             }
             else
             {
-                if (thisArgument.IsNullOrUndefined())
+                if (thisArgument is null || thisArgument.IsNullOrUndefined())
                 {
                     var globalEnv = calleeRealm.GlobalEnv;
                     thisValue = globalEnv.GlobalThisValue;

+ 2 - 1
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -138,7 +138,8 @@ namespace Jint.Native.Function
             {
                 try
                 {
-                    var result = OrdinaryCallEvaluateBody(_engine._activeEvaluationContext, arguments, calleeContext);
+                    var context = _engine._activeEvaluationContext ?? new EvaluationContext(_engine);
+                    var result = OrdinaryCallEvaluateBody(context, arguments, calleeContext);
 
                     // The DebugHandler needs the current execution context before the return for stepping through the return point
                     if (_engine._isDebugMode && result.Type != CompletionType.Throw)

+ 2 - 3
Jint/Native/Object/ObjectInstance.cs

@@ -959,10 +959,9 @@ namespace Jint.Native.Object
                     break;
 
                 case ObjectClass.Function:
-                    if (this is FunctionInstance function)
+                    if (this is ICallable function)
                     {
-                        converted = new Func<JsValue, JsValue[], JsValue>(
-                            (thisVal, args) => function.Engine.Invoke(function, (object) thisVal, args));
+                        converted = (Func<JsValue, JsValue[], JsValue>) function.Call;
                     }
 
                     break;