Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Invoke logic and object coercion #883

Merged
merged 1 commit into from
May 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 29 additions & 2 deletions Jint.Tests/Runtime/FunctionTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System;
using Jint.Native;
using Jint.Runtime;
using Jint.Runtime.Interop;
using Xunit;

namespace Jint.Tests.Runtime
Expand All @@ -10,7 +13,7 @@ public void BindCombinesBoundArgumentsToCallArgumentsCorrectly()
{
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());
}
Expand Down Expand Up @@ -40,7 +43,7 @@ public void ArrowFunctionShouldBeExtensible()
assert(a.foo === 'bar');
");
}

[Fact]
public void BlockScopeFunctionShouldWork()
{
Expand All @@ -67,5 +70,29 @@ public void BlockScopeFunctionShouldWork()

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 changes: 19 additions & 0 deletions Jint/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,25 @@ public JsValue Invoke(JsValue value, object thisObj, object[] arguments)
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>
Expand Down
41 changes: 8 additions & 33 deletions Jint/Native/JsValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public bool IsRegExp()
{
return false;
}

var matcher = oi.Get(GlobalSymbolRegistry.Match);
if (!matcher.IsUndefined())
{
Expand Down Expand Up @@ -251,7 +251,7 @@ public RegExpInstance AsRegExp()

return null;
}

[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T As<T>() where T : ObjectInstance
Expand Down Expand Up @@ -375,38 +375,13 @@ private static JsValue Convert(Engine e, object v)
/// <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)
{
Expand Down Expand Up @@ -446,17 +421,17 @@ public override string ToString()

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

return true;
}

if ((object)b == null)
if ((object) b == null)
{
return true;
}
Expand Down Expand Up @@ -588,7 +563,7 @@ internal virtual JsValue DoClone()
{
return this;
}

internal virtual bool IsCallable => this is ICallable;

internal static bool SameValue(JsValue x, JsValue y)
Expand Down
16 changes: 8 additions & 8 deletions Jint/Native/String/StringPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ private JsValue Split(JsValue thisObj, JsValue[] arguments)
{
separator = JsString.Empty;
}

if (separator is ObjectInstance oi)
{
var splitter = GetMethod(_engine, oi, GlobalSymbolRegistry.Split);
Expand Down Expand Up @@ -446,7 +446,7 @@ private JsValue Search(JsValue thisObj, JsValue[] arguments)
{
TypeConverter.CheckObjectCoercible(Engine, thisObj);
var regex = arguments.At(0);

if (regex is ObjectInstance oi)
{
var searcher = GetMethod(_engine, oi, GlobalSymbolRegistry.Search);
Expand All @@ -458,7 +458,7 @@ private JsValue Search(JsValue thisObj, JsValue[] arguments)

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)
Expand All @@ -476,7 +476,7 @@ private JsValue Replace(JsValue thisObj, JsValue[] arguments)
return replacer.Call(searchValue, new[] { thisObj, replaceValue});
}
}

var thisString = TypeConverter.ToJsString(thisObj);
var searchString = TypeConverter.ToString(searchValue);
var functionalReplace = replaceValue is ICallable;
Expand Down Expand Up @@ -524,11 +524,11 @@ private JsValue Match(JsValue thisObj, JsValue[] arguments)
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)
Expand All @@ -553,11 +553,11 @@ private JsValue MatchAll(JsValue thisObj, JsValue[] arguments)
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)
Expand Down