using System;
using System.Collections.Generic;
using System.Linq;
using Jint.Native.Object;
using Jint.Parser;
using Jint.Parser.Ast;
using Jint.Runtime;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Environments;
namespace Jint.Native.Function
{
///
///
///
public sealed class FunctionConstructor : FunctionInstance, IConstructor
{
private FunctionConstructor(Engine engine):base(engine, null, null, false)
{
}
public static FunctionConstructor CreateFunctionConstructor(Engine engine)
{
var obj = new FunctionConstructor(engine);
// 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.FastAddProperty("prototype", obj.PrototypeObject, false, false, false);
obj.FastAddProperty("length", 1, false, false, false);
return obj;
}
public void Configure()
{
}
public FunctionPrototype PrototypeObject { get; private set; }
public override object Call(object thisObject, object[] arguments)
{
return Construct(arguments);
}
public ObjectInstance Construct(object[] 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]);
}
body = TypeConverter.ToString(body);
var parser = new JavaScriptParser();
// todo: ensure parsable as parameter list
var parameters = p.Split(new [] {','}, StringSplitOptions.RemoveEmptyEntries);
var statements = parser.ParseFunctionBody(body);
var functionObject = new ScriptFunctionInstance(
Engine,
new FunctionDeclaration
{
Type = SyntaxNodes.FunctionDeclaration,
Body = new BlockStatement
{
Type = SyntaxNodes.BlockStatement,
Body = statements
},
Parameters = parameters.Select(x => new Identifier
{
Type = SyntaxNodes.Identifier,
Name = x
}),
FunctionDeclarations = new List(),
VariableDeclarations = new List()
},
LexicalEnvironment.NewDeclarativeEnvironment(Engine, Engine.ExecutionContext.LexicalEnvironment),
false
) { 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;
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