Skip to content

Commit

Permalink
Bound function is not a FunctionInstance
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma committed Jan 9, 2022
1 parent a6c09a9 commit 21e3bee
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 33 deletions.
56 changes: 39 additions & 17 deletions Jint/Native/Function/BindFunctionInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,46 @@

namespace Jint.Native.Function
{
public sealed class BindFunctionInstance : FunctionInstance, IConstructor
/// <summary>
/// https://tc39.es/ecma262/#sec-bound-function-exotic-objects
/// </summary>
internal sealed class BindFunctionInstance : ObjectInstance, IConstructor, ICallable
{
public BindFunctionInstance(
Engine engine,
Realm realm)
: base(engine, realm, name: null, thisMode: FunctionThisMode.Strict)
private readonly Realm _realm;

public BindFunctionInstance(Engine engine,
Realm realm,
ObjectInstance proto,
ObjectInstance targetFunction,
JsValue boundThis,
JsValue[] boundArgs)
: base(engine, ObjectClass.Function)
{
_realm = realm;
_prototype = proto;
BoundTargetFunction = targetFunction;
BoundThis = boundThis;
BoundArguments = boundArgs;
}

public JsValue TargetFunction { get; set; }
/// <summary>
/// The wrapped function object.
/// </summary>
public JsValue BoundTargetFunction { get; }

public JsValue BoundThis { get; set; }
/// <summary>
/// The value that is always passed as the this value when calling the wrapped function.
/// </summary>
public JsValue BoundThis { get; }

public JsValue[] BoundArgs { get; set; }
/// <summary>
/// A list of values whose elements are used as the first arguments to any call to the wrapped function.
/// </summary>
public JsValue[] BoundArguments { get; }

public override JsValue Call(JsValue thisObject, JsValue[] arguments)
public JsValue Call(JsValue thisObject, JsValue[] arguments)
{
var f = TargetFunction as FunctionInstance;
var f = BoundTargetFunction as FunctionInstance;
if (f is null)
{
ExceptionHelper.ThrowTypeError(_realm);
Expand All @@ -35,7 +57,7 @@ public override JsValue Call(JsValue thisObject, JsValue[] arguments)

public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
{
var target = TargetFunction as IConstructor;
var target = BoundTargetFunction as IConstructor;
if (target is null)
{
ExceptionHelper.ThrowTypeError(_realm);
Expand All @@ -45,7 +67,7 @@ public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)

if (ReferenceEquals(this, newTarget))
{
newTarget = TargetFunction;
newTarget = BoundTargetFunction;
}

var value = target.Construct(args, newTarget);
Expand All @@ -56,7 +78,7 @@ public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)

internal override bool OrdinaryHasInstance(JsValue v)
{
var f = TargetFunction as FunctionInstance;
var f = BoundTargetFunction as FunctionInstance;
if (f is null)
{
ExceptionHelper.ThrowTypeError(_realm);
Expand All @@ -67,13 +89,13 @@ internal override bool OrdinaryHasInstance(JsValue v)

private JsValue[] CreateArguments(JsValue[] arguments)
{
var combined = _engine._jsValueArrayPool.RentArray(BoundArgs.Length + arguments.Length);
System.Array.Copy(BoundArgs, combined, BoundArgs.Length);
System.Array.Copy(arguments, 0, combined, BoundArgs.Length, arguments.Length);
var combined = _engine._jsValueArrayPool.RentArray(BoundArguments.Length + arguments.Length);
System.Array.Copy(BoundArguments, combined, BoundArguments.Length);
System.Array.Copy(arguments, 0, combined, BoundArguments.Length, arguments.Length);
return combined;
}

internal override bool IsConstructor => TargetFunction.IsConstructor;
internal override bool IsConstructor => BoundTargetFunction.IsConstructor;

public override string ToString() => "function () { [native code] }";
}
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/Function/FunctionInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ internal Realm GetFunctionRealm(JsValue obj)

if (obj is BindFunctionInstance bindFunctionInstance)
{
return GetFunctionRealm(bindFunctionInstance.TargetFunction);
return GetFunctionRealm(bindFunctionInstance.BoundTargetFunction);
}

if (obj is ProxyInstance proxyInstance)
Expand Down
19 changes: 4 additions & 15 deletions Jint/Native/Function/FunctionPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,7 @@ protected override void Initialize()

private static JsValue HasInstance(JsValue thisObj, JsValue[] arguments)
{
if (thisObj is not FunctionInstance f)
{
return false;
}

return f.OrdinaryHasInstance(arguments.At(0));
return thisObj.OrdinaryHasInstance(arguments.At(0));
}

private JsValue Bind(JsValue thisObj, JsValue[] arguments)
Expand Down Expand Up @@ -88,7 +83,7 @@ private JsValue Bind(JsValue thisObj, JsValue[] arguments)
l = JsNumber.PositiveZero;
}

f._length = new PropertyDescriptor(l, PropertyFlag.Configurable);
f.DefinePropertyOrThrow(CommonProperties.Length, new PropertyDescriptor(l, PropertyFlag.Configurable));

var targetName = thisObj.Get(CommonProperties.Name);
if (!targetName.IsString())
Expand All @@ -104,16 +99,10 @@ private JsValue Bind(JsValue thisObj, JsValue[] arguments)
/// <summary>
/// https://tc39.es/ecma262/#sec-boundfunctioncreate
/// </summary>
private FunctionInstance BoundFunctionCreate(ObjectInstance targetFunction, JsValue boundThis, JsValue[] boundArgs)
private BindFunctionInstance BoundFunctionCreate(ObjectInstance targetFunction, JsValue boundThis, JsValue[] boundArgs)
{
var proto = targetFunction.GetPrototypeOf();
var obj = new BindFunctionInstance(_engine, _realm)
{
_prototype = proto,
TargetFunction = targetFunction,
BoundThis = boundThis,
BoundArgs = boundArgs
};
var obj = new BindFunctionInstance(_engine, _realm, proto, targetFunction, boundThis, boundArgs);
return obj;
}

Expand Down
19 changes: 19 additions & 0 deletions Jint/Native/Object/ObjectInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,25 @@ public virtual bool SetPrototypeOf(JsValue value)
return true;
}

/// <summary>
/// https://tc39.es/ecma262/#sec-setfunctionname
/// </summary>
internal void SetFunctionName(JsValue name, string prefix = null)
{
if (name is JsSymbol symbol)
{
name = symbol._value.IsUndefined()
? JsString.Empty
: new JsString("[" + symbol._value + "]");
}
if (!string.IsNullOrWhiteSpace(prefix))
{
name = prefix + " " + name;
}

DefinePropertyOrThrow(CommonProperties.Name, new PropertyDescriptor(name, PropertyFlag.Configurable));
}

/// <summary>
/// https://tc39.es/ecma262/#sec-createmethodproperty
/// </summary>
Expand Down

0 comments on commit 21e3bee

Please sign in to comment.