浏览代码

Fix Invoke logic and object coercion (#883)

Marko Lahma 4 年之前
父节点
当前提交
dcaae15a02
共有 4 个文件被更改,包括 64 次插入43 次删除
  1. 29 2
      Jint.Tests/Runtime/FunctionTests.cs
  2. 19 0
      Jint/Engine.cs
  3. 8 33
      Jint/Native/JsValue.cs
  4. 8 8
      Jint/Native/String/StringPrototype.cs

+ 29 - 2
Jint.Tests/Runtime/FunctionTests.cs

@@ -1,4 +1,7 @@
 using System;
+using Jint.Native;
+using Jint.Runtime;
+using Jint.Runtime.Interop;
 using Xunit;
 
 namespace Jint.Tests.Runtime
@@ -10,7 +13,7 @@ namespace Jint.Tests.Runtime
         {
             var e = new Engine();
             e.Execute("var testFunc = function (a, b, c) { return a + ', ' + b + ', ' + c + ', ' + JSON.stringify(arguments); }");
-            
+
             Assert.Equal("a, 1, a, {\"0\":\"a\",\"1\":1,\"2\":\"a\"}", e.Execute("testFunc('a', 1, 'a');").GetCompletionValue().AsString());
             Assert.Equal("a, 1, a, {\"0\":\"a\",\"1\":1,\"2\":\"a\"}", e.Execute("testFunc.bind('anything')('a', 1, 'a');").GetCompletionValue().AsString());
         }
@@ -40,7 +43,7 @@ namespace Jint.Tests.Runtime
                     assert(a.foo === 'bar');
                 ");
         }
-        
+
         [Fact]
         public void BlockScopeFunctionShouldWork()
         {
@@ -67,5 +70,29 @@ function execute(doc, args){
 
             Assert.Equal("ayende", obj.Get("Name").AsString());
         }
+
+        [Fact]
+        public void ObjectCoercibleForCallable()
+        {
+            const string script = @"
+var booleanCount = 0;
+Boolean.prototype.then = function() {
+  booleanCount += 1;
+};
+function test() {
+    this.then();    
+}
+testFunction.call(true);
+assertEqual(booleanCount, 1);
+";
+            var engine = new Engine();
+            engine
+                .SetValue("testFunction", new ClrFunctionInstance(engine, "testFunction", (thisValue, args) =>
+                {
+                    return engine.Invoke(thisValue, "then", new[] { Undefined.Instance, args.At(0) });
+                }))
+                .SetValue("assertEqual", new Action<object, object>((a, b) => Assert.Equal(b, a)))
+                .Execute(script);
+        }
     }
 }

+ 19 - 0
Jint/Engine.cs

@@ -643,6 +643,25 @@ namespace Jint
             return result;
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-invoke
+        /// </summary>
+        internal JsValue Invoke(JsValue v, JsValue p, JsValue[] arguments)
+        {
+            var func = GetV(v, p);
+            var callable = func as ICallable ?? ExceptionHelper.ThrowTypeErrorNoEngine<ICallable>("Can only invoke functions");
+            return callable.Call(v, arguments);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-getv
+        /// </summary>
+        internal JsValue GetV(JsValue v, JsValue p)
+        {
+            var o = TypeConverter.ToObject(this, v);
+            return o.Get(p);
+        }
+
         /// <summary>
         /// Gets a named value from the Global scope.
         /// </summary>

+ 8 - 33
Jint/Native/JsValue.cs

@@ -76,7 +76,7 @@ namespace Jint.Native
             {
                 return false;
             }
-            
+
             var matcher = oi.Get(GlobalSymbolRegistry.Match);
             if (!matcher.IsUndefined())
             {
@@ -251,7 +251,7 @@ namespace Jint.Native
 
             return null;
         }
-        
+
         [Pure]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public T As<T>() where T : ObjectInstance
@@ -375,38 +375,13 @@ namespace Jint.Native
         /// <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>
-        internal JsValue Invoke(JsValue thisObj, JsValue[] arguments)
         {
             var callable = this as ICallable ?? ExceptionHelper.ThrowTypeErrorNoEngine<ICallable>("Can only invoke functions");
-            return callable.Call(thisObj, arguments);
-        }
-        
-        /// <summary>
-        /// Invoke the given property as function.
-        /// </summary>
-        /// <param name="v">Serves as both the lookup point for the property and the this value of the call</param>
-        /// <param name="propertyName">Property that should be ICallable</param>
-        /// <param name="arguments">The arguments of the function call.</param>
-        /// <returns>The value returned by the function call.</returns>
-        internal static JsValue Invoke(JsValue v, JsValue propertyName, JsValue[] arguments)
-        {
-            var func = v.Get(propertyName);
-            var callable = func as ICallable ?? ExceptionHelper.ThrowTypeErrorNoEngine<ICallable>("Can only invoke functions");
-            return callable.Call(v, arguments);
+            return callable.Call(Undefined, arguments);
         }
 
         public virtual bool HasOwnProperty(JsValue property) => false;
-        
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public JsValue Get(JsValue property)
         {
@@ -446,9 +421,9 @@ namespace Jint.Native
 
         public static bool operator !=(JsValue a, JsValue b)
         {
-            if ((object)a == null)
+            if ((object) a == null)
             {
-                if ((object)b == null)
+                if ((object) b == null)
                 {
                     return false;
                 }
@@ -456,7 +431,7 @@ namespace Jint.Native
                 return true;
             }
 
-            if ((object)b == null)
+            if ((object) b == null)
             {
                 return true;
             }
@@ -588,7 +563,7 @@ namespace Jint.Native
         {
             return this;
         }
-        
+
         internal virtual bool IsCallable => this is ICallable;
 
         internal static bool SameValue(JsValue x, JsValue y)

+ 8 - 8
Jint/Native/String/StringPrototype.cs

@@ -323,7 +323,7 @@ namespace Jint.Native.String
             {
                 separator = JsString.Empty;
             }
-            
+
             if (separator is ObjectInstance oi)
             {
                 var splitter = GetMethod(_engine, oi, GlobalSymbolRegistry.Split);
@@ -446,7 +446,7 @@ namespace Jint.Native.String
         {
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
             var regex = arguments.At(0);
-            
+
             if (regex is ObjectInstance oi)
             {
                 var searcher = GetMethod(_engine, oi, GlobalSymbolRegistry.Search);
@@ -458,7 +458,7 @@ namespace Jint.Native.String
 
             var rx = (RegExpInstance) Engine.RegExp.Construct(new[] {regex});
             var s = TypeConverter.ToString(thisObj);
-            return Invoke(rx, GlobalSymbolRegistry.Search, new JsValue[] { s });
+            return _engine.Invoke(rx, GlobalSymbolRegistry.Search, new JsValue[] { s });
         }
 
         private JsValue Replace(JsValue thisObj, JsValue[] arguments)
@@ -476,7 +476,7 @@ namespace Jint.Native.String
                     return replacer.Call(searchValue, new[] { thisObj, replaceValue});
                 }
             }
-            
+
             var thisString = TypeConverter.ToJsString(thisObj);
             var searchString = TypeConverter.ToString(searchValue);
             var functionalReplace = replaceValue is ICallable;
@@ -524,11 +524,11 @@ namespace Jint.Native.String
                     return matcher.Call(regex, new[] { thisObj });
                 }
             }
-            
+
             var rx = (RegExpInstance) Engine.RegExp.Construct(new[] {regex});
 
             var s = TypeConverter.ToString(thisObj);
-            return Invoke(rx, GlobalSymbolRegistry.Match, new JsValue[] { s });
+            return _engine.Invoke(rx, GlobalSymbolRegistry.Match, new JsValue[] { s });
         }
 
         private JsValue MatchAll(JsValue thisObj, JsValue[] arguments)
@@ -553,11 +553,11 @@ namespace Jint.Native.String
                     return matcher.Call(regex, new[] { thisObj });
                 }
             }
-            
+
             var s = TypeConverter.ToString(thisObj);
             var rx = (RegExpInstance) Engine.RegExp.Construct(new[] { regex, "g" });
 
-            return Invoke(rx, GlobalSymbolRegistry.MatchAll, new JsValue[] { s });
+            return _engine.Invoke(rx, GlobalSymbolRegistry.MatchAll, new JsValue[] { s });
         }
 
         private JsValue LocaleCompare(JsValue thisObj, JsValue[] arguments)