Browse Source

Implementing Invoke methods on Engine and JsValue

Sebastien Ros 11 years ago
parent
commit
31709f9282
4 changed files with 135 additions and 8 deletions
  1. 39 5
      Jint.Tests/Runtime/EngineTests.cs
  2. 20 1
      Jint.Tests/Runtime/InteropTests.cs
  3. 47 2
      Jint/Engine.cs
  4. 29 0
      Jint/Native/JsValue.cs

+ 39 - 5
Jint.Tests/Runtime/EngineTests.cs

@@ -10,18 +10,27 @@ using Xunit.Extensions;
 
 namespace Jint.Tests.Runtime
 {
-    public class EngineTests
+    public class EngineTests : IDisposable
     {
-        private Engine RunTest(string source)
+        private readonly Engine _engine;
+
+        public EngineTests()
         {
-            var engine = new Engine()
+            _engine = new Engine()
                 .SetValue("log", new Action<object>(Console.WriteLine))
                 .SetValue("assert", new Action<bool>(Assert.True))
                 ;
+        }
+
+        void IDisposable.Dispose()
+        {
+        }
+
 
-            engine.Execute(source);
+        private void RunTest(string source)
+        {
 
-            return engine;
+            _engine.Execute(source);
         }
 
         [Theory]
@@ -662,5 +671,30 @@ namespace Jint.Tests.Runtime
             }
             Console.WriteLine("{0}: {1}", result, standard = sw.ElapsedMilliseconds);
         }
+
+        [Fact]
+        public void ShouldInvokeAFunctionValue()
+        {
+            RunTest(@"
+                function add(x, y) { return x + y; }
+            ");
+
+            var add = _engine.GetValue("add");
+
+            Assert.Equal(3, add.Invoke(1, 2));
+        }
+
+
+        [Fact]
+        public void ShouldNotInvokeNonFunctionValue()
+        {
+            RunTest(@"
+                var x= 10;
+            ");
+
+            var x = _engine.GetValue("x");
+
+            Assert.Throws<ArgumentException>(() => x.Invoke(1, 2));
+        }
     }
 }

+ 20 - 1
Jint.Tests/Runtime/InteropTests.cs

@@ -387,7 +387,7 @@ namespace Jint.Tests.Runtime
         public void ShouldConvertObjectInstanceToExpando()
         {
             _engine.Execute("var o = {a: 1, b: 'foo'}");
-            var result = _engine.GetGlobalValue("o");
+            var result = _engine.GetValue("o");
 
             dynamic value = result.ToObject();
 
@@ -481,5 +481,24 @@ namespace Jint.Tests.Runtime
             ");
         }
 
+        [Fact]
+        public void ShouldInvokeAFunctionByName()
+        {
+            RunTest(@"
+                function add(x, y) { return x + y; }
+            ");
+
+            Assert.Equal(3, _engine.Invoke("add", 1, 2));
+        }
+
+        [Fact]
+        public void ShouldNotInvokeNonFunctionValue()
+        {
+            RunTest(@"
+                var x= 10;
+            ");
+
+            Assert.Throws<ArgumentException>(() => _engine.Invoke("x", 1, 2));
+        }
     }
 }

+ 47 - 2
Jint/Engine.cs

@@ -546,15 +546,60 @@ namespace Jint
             }
         }
 
+        /// <summary>
+        /// Invoke the current value as function.
+        /// </summary>
+        /// <param name="propertyName">The arguments of the function call.</param>
+        /// <returns>The value returned by the function call.</returns>
+        public JsValue Invoke(string propertyName, params object[] arguments)
+        {
+            return Invoke(propertyName, null, arguments);
+        }
 
-        public JsValue GetGlobalValue(string propertyName)
+        /// <summary>
+        /// Invoke the current value as function.
+        /// </summary>
+        /// <param name="propertyName">The name of the function to call.</param>
+        /// <param name="thisObj">The this value inside the function call.</param>
+        /// <param name="arguments">The arguments of the function call.</param>
+        /// <returns>The value returned by the function call.</returns>
+        public JsValue Invoke(string propertyName, object thisObj, object[] arguments)
+        {
+            var value = GetValue(propertyName);
+            var callable = value.TryCast<ICallable>();
+
+            if (callable == null)
+            {
+                throw new ArgumentException("Can only invoke functions");
+            }
+
+            return callable.Call(JsValue.FromObject(this, thisObj), arguments.Select(x => JsValue.FromObject(this, x)).ToArray());
+        }
+
+        /// <summary>
+        /// Gets a named value from the Global scope.
+        /// </summary>
+        /// <param name="propertyName">The name of the property to return.</param>
+        public JsValue GetValue(string propertyName)
+        {
+            return GetValue(Global, propertyName);
+        }
+
+        /// <summary>
+        /// Gets a named value from the specified scope.
+        /// </summary>
+        /// <param name="scope">The scope to get the property from.</param>
+        /// <param name="propertyName">The name of the property to return.</param>
+        public JsValue GetValue(JsValue scope, string propertyName)
         {
             if (System.String.IsNullOrEmpty(propertyName))
             {
                 throw new ArgumentException("propertyName");
             }
 
-            return GetValue(Global.Get(propertyName));
+            var reference = new Reference(scope, propertyName, Options.IsStrict());
+
+            return GetValue(reference);
         }
 
         //  http://www.ecma-international.org/ecma-262/5.1/#sec-10.5

+ 29 - 0
Jint/Native/JsValue.cs

@@ -4,6 +4,7 @@ using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics.Contracts;
 using System.Dynamic;
+using System.Linq;
 using Jint.Native.Array;
 using Jint.Native.Boolean;
 using Jint.Native.Date;
@@ -491,6 +492,34 @@ namespace Jint.Native
             }
         }
 
+        /// <summary>
+        /// Invoke the current value as function.
+        /// </summary>
+        /// <param name="arguments">The arguments of the function call.</param>
+        /// <returns>The value returned by the function call.</returns>
+        public JsValue Invoke(params JsValue[] arguments)
+        {
+            return Invoke(Undefined, arguments);
+        }
+
+        /// <summary>
+        /// Invoke the current value as function.
+        /// </summary>
+        /// <param name="thisObj">The this value inside the function call.</param>
+        /// <param name="arguments">The arguments of the function call.</param>
+        /// <returns>The value returned by the function call.</returns>
+        public JsValue Invoke(JsValue thisObj, JsValue[] arguments)
+        {
+            var callable = TryCast<ICallable>();
+
+            if (callable == null)
+            {
+                throw new ArgumentException("Can only invoke functions");
+            }
+
+            return callable.Call(thisObj, arguments);
+        }
+
         public override string ToString()
         {
             switch (Type)