using System;
using Esprima;
using Esprima.Ast;
using Jint.Native.Object;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Environments;
namespace Jint.Native.Function
{
public sealed class FunctionConstructor : FunctionInstance, IConstructor
{
private static readonly ParserOptions ParserOptions = new ParserOptions { AdaptRegexp = true, Tolerant = false };
private FunctionConstructor(Engine engine):base(engine, null, null, false)
{
}
public static FunctionConstructor CreateFunctionConstructor(Engine engine)
{
var obj = new FunctionConstructor(engine);
obj.Extensible = true;
// The initial value of Function.prototype is the standard built-in Function prototype object
obj.PrototypeObject = FunctionPrototype.CreatePrototypeObject(engine);
// The value of the [[Prototype]] internal property of the Function constructor is the standard built-in Function prototype object
obj.Prototype = obj.PrototypeObject;
obj.SetOwnProperty("prototype", new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden));
obj.SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.AllForbidden));
return obj;
}
public void Configure()
{
}
public FunctionPrototype PrototypeObject { get; private set; }
public override JsValue Call(JsValue thisObject, JsValue[] arguments)
{
return Construct(arguments);
}
public ObjectInstance Construct(JsValue[] arguments)
{
var argCount = arguments.Length;
string p = "";
string body = "";
if (argCount == 1)
{
body = TypeConverter.ToString(arguments[0]);
}
else if (argCount > 1)
{
var firstArg = arguments[0];
p = TypeConverter.ToString(firstArg);
for (var k = 1; k < argCount - 1; k++)
{
var nextArg = arguments[k];
p += "," + TypeConverter.ToString(nextArg);
}
body = TypeConverter.ToString(arguments[argCount-1]);
}
IFunction function;
try
{
var functionExpression = "function f(" + p + ") { " + body + "}";
var parser = new JavaScriptParser(functionExpression, ParserOptions);
function = (IFunction) parser.ParseProgram().Body[0];
}
catch (ParserException)
{
throw new JavaScriptException(Engine.SyntaxError);
}
var functionObject = new ScriptFunctionInstance(
Engine,
function,
LexicalEnvironment.NewDeclarativeEnvironment(Engine, Engine.ExecutionContext.LexicalEnvironment),
function.Strict
) { Extensible = true };
return functionObject;
}
///
/// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2
///
///
///
public FunctionInstance CreateFunctionObject(FunctionDeclaration functionDeclaration)
{
var functionObject = new ScriptFunctionInstance(
Engine,
functionDeclaration,
LexicalEnvironment.NewDeclarativeEnvironment(Engine, Engine.ExecutionContext.LexicalEnvironment),
functionDeclaration.Strict
) { Extensible = true };
return functionObject;
}
private FunctionInstance _throwTypeError;
private static readonly char[] ArgumentNameSeparator = new[] { ',' };
public FunctionInstance ThrowTypeError
{
get
{
if (!ReferenceEquals(_throwTypeError, null))
{
return _throwTypeError;
}
_throwTypeError = new ThrowTypeError(Engine);
return _throwTypeError;
}
}
public object Apply(JsValue thisObject, JsValue[] arguments)
{
if (arguments.Length != 2)
{
throw new ArgumentException("Apply has to be called with two arguments.");
}
var func = thisObject.TryCast();
var thisArg = arguments[0];
var argArray = arguments[1];
if (func == null)
{
throw new JavaScriptException(Engine.TypeError);
}
if (argArray.IsNull() || argArray.IsUndefined())
{
return func.Call(thisArg, Arguments.Empty);
}
var argArrayObj = argArray.TryCast();
if (ReferenceEquals(argArrayObj, null))
{
throw new JavaScriptException(Engine.TypeError);
}
var len = argArrayObj.Get("length");
var n = TypeConverter.ToUint32(len);
var argList = new JsValue[n];
for (var index = 0; index < n; index++)
{
var indexName = TypeConverter.ToString(index);
var nextArg = argArrayObj.Get(indexName);
argList[index] = nextArg;
}
return func.Call(thisArg, argList);
}
}
}