using Jint.Collections;
using Jint.Native.Array;
using Jint.Native.Object;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Interop;
namespace Jint.Native.Function
{
///
/// http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.4
///
public sealed class FunctionPrototype : FunctionInstance
{
private static readonly JsString _functionName = new JsString("Function");
private FunctionPrototype(Engine engine)
: base(engine, _functionName)
{
}
public static FunctionPrototype CreatePrototypeObject(Engine engine)
{
var obj = new FunctionPrototype(engine)
{
// The value of the [[Prototype]] internal property of the Function prototype object is the standard built-in Object prototype object
_prototype = engine.Object.PrototypeObject,
_length = PropertyDescriptor.AllForbiddenDescriptor.NumberZero
};
return obj;
}
protected override void Initialize()
{
var properties = new PropertyDictionary(5, checkExistingKeys: false)
{
["constructor"] = new PropertyDescriptor(Engine.Function, PropertyFlag.NonEnumerable),
["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToString), true, false, true),
["apply"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "apply", Apply, 2), true, false, true),
["call"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "call", CallImpl, 1), true, false, true),
["bind"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "bind", Bind, 1), true, false, true)
};
SetProperties(properties);
}
private JsValue Bind(JsValue thisObj, JsValue[] arguments)
{
var target = thisObj.TryCast(x =>
{
ExceptionHelper.ThrowTypeError(Engine);
});
var thisArg = arguments.At(0);
var f = new BindFunctionInstance(Engine)
{
TargetFunction = thisObj,
BoundThis = thisArg,
BoundArgs = arguments.Skip(1),
_prototype = Engine.Function.PrototypeObject
};
if (target is FunctionInstance functionInstance)
{
var l = TypeConverter.ToNumber(functionInstance.Get(CommonProperties.Length, functionInstance)) - (arguments.Length - 1);
f.SetOwnProperty(CommonProperties.Length, new PropertyDescriptor(System.Math.Max(l, 0), PropertyFlag.AllForbidden));
}
else
{
f.SetOwnProperty(CommonProperties.Length, PropertyDescriptor.AllForbiddenDescriptor.NumberZero);
}
f.DefineOwnProperty(CommonProperties.Caller, _engine._getSetThrower);
f.DefineOwnProperty(CommonProperties.Arguments, _engine._getSetThrower);
return f;
}
private JsValue ToString(JsValue thisObj, JsValue[] arguments)
{
if (!(thisObj is FunctionInstance))
{
return ExceptionHelper.ThrowTypeError(_engine, "Function object expected.");
}
return "function() {{ ... }}";
}
internal JsValue Apply(JsValue thisObject, JsValue[] arguments)
{
var func = thisObject as ICallable ?? ExceptionHelper.ThrowTypeError(Engine);
var thisArg = arguments.At(0);
var argArray = arguments.At(1);
if (argArray.IsNullOrUndefined())
{
return func.Call(thisArg, Arguments.Empty);
}
var argList = CreateListFromArrayLike(argArray);
var result = func.Call(thisArg, argList);
return result;
}
internal JsValue[] CreateListFromArrayLike(JsValue argArray, Types? elementTypes = null)
{
var argArrayObj = argArray as ObjectInstance ?? ExceptionHelper.ThrowTypeError(_engine);
var operations = ArrayOperations.For(argArrayObj);
var allowedTypes = elementTypes ??
Types.Undefined | Types.Null | Types.Boolean | Types.String | Types.Symbol | Types.Number | Types.Object;
var argList = operations.GetAll(allowedTypes);
return argList;
}
private JsValue CallImpl(JsValue thisObject, JsValue[] arguments)
{
var func = thisObject as ICallable ?? ExceptionHelper.ThrowTypeError(Engine);
JsValue[] values = System.Array.Empty();
if (arguments.Length > 1)
{
values = new JsValue[arguments.Length - 1];
System.Array.Copy(arguments, 1, values, 0, arguments.Length - 1);
}
var result = func.Call(arguments.At(0), values);
return result;
}
public override JsValue Call(JsValue thisObject, JsValue[] arguments)
{
return Undefined;
}
}
}