using System;
using System.Collections.Generic;
using Jint.Native.Object;
using Jint.Parser.Ast;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Descriptors.Specialized;
using Jint.Runtime.Environments;
namespace Jint.Native.Function
{
///
///
///
public sealed class FunctionConstructor : FunctionInstance, IConstructor
{
private readonly Engine _engine;
public FunctionConstructor(Engine engine)
: base(engine, engine.RootFunction, null, null, false)
{
_engine = engine;
// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2
Extensible = true;
// Function.prototype properties
engine.RootFunction.DefineOwnProperty("apply", new ClrDataDescriptor(engine, Apply), false);
}
public override object Call(object thisObject, object[] arguments)
{
return Construct(arguments);
}
public ObjectInstance Construct(object[] arguments)
{
var instance = new FunctionShim(_engine, Prototype, null, _engine.GlobalEnvironment);
instance.DefineOwnProperty("constructor", new DataDescriptor(Prototype) { Writable = true, Enumerable = false, Configurable = false }, false);
return instance;
}
///
/// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2
///
///
///
public FunctionInstance CreateFunctionObject(FunctionDeclaration functionDeclaration)
{
var functionObject = new ScriptFunctionInstance(
_engine,
functionDeclaration,
_engine.Function.Prototype /* instancePrototype */,
_engine.Object.Construct(Arguments.Empty) /* functionPrototype */,
LexicalEnvironment.NewDeclarativeEnvironment(_engine, _engine.ExecutionContext.LexicalEnvironment),
functionDeclaration.Strict
) { Extensible = true };
return functionObject;
}
private FunctionInstance _throwTypeError;
public FunctionInstance ThrowTypeError
{
get
{
if (_throwTypeError != null)
{
return _throwTypeError;
}
_throwTypeError = new ThrowTypeError(_engine);
return _throwTypeError;
}
}
public object Apply(object thisObject, object[] arguments)
{
if (arguments.Length != 2)
{
throw new ArgumentException("Apply has to be called with two arguments.");
}
var func = thisObject as ICallable;
var thisArg = arguments[0];
var argArray = arguments[1];
if (func == null)
{
throw new JavaScriptException(_engine.TypeError);
}
if (argArray == Null.Instance || argArray == Undefined.Instance)
{
return func.Call(thisArg, Arguments.Empty);
}
var argArrayObj = argArray as ObjectInstance;
if (argArrayObj == null)
{
throw new JavaScriptException(_engine.TypeError);
}
var len = argArrayObj.Get("length");
var n = TypeConverter.ToUint32(len);
var argList = new List();
for (var index = 0; index < n; index++)
{
var indexName = index.ToString();
var nextArg = argArrayObj.Get(indexName);
argList.Add(nextArg);
}
return func.Call(thisArg, argList.ToArray());
}
}
}