using System; using System.Collections.Generic; using System.Linq; using Jint.Native.Object; using Jint.Native.String; using Jint.Parser; using Jint.Parser.Ast; using Jint.Runtime; 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); 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.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 JsValue Call(JsValue thisObject, JsValue[] arguments) { return Construct(arguments); } private string[] ParseArgumentNames(string parameterDeclaration) { string[] values = parameterDeclaration.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var newValues = new string[values.Length]; for (var i = 0; i < values.Length; i++) { newValues[i] = StringPrototype.TrimEx(values[i]); } return newValues; } 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]); } var parameters = this.ParseArgumentNames(p); var parser = new JavaScriptParser(); FunctionExpression function; try { var functionExpression = "function(" + p + ") { " + body + "}"; function = parser.ParseFunctionExpression(functionExpression); } catch (ParserException) { throw new JavaScriptException(Engine.SyntaxError); } // todo: check if there is not a way to use the FunctionExpression directly instead of creating a FunctionDeclaration var functionObject = new ScriptFunctionInstance( Engine, new FunctionDeclaration { Type = SyntaxNodes.FunctionDeclaration, Body = new BlockStatement { Type = SyntaxNodes.BlockStatement, Body = new [] { function.Body } }, Parameters = parameters.Select(x => new Identifier { Type = SyntaxNodes.Identifier, Name = x }).ToArray(), FunctionDeclarations = function.FunctionDeclarations, VariableDeclarations = function.VariableDeclarations }, 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; public FunctionInstance ThrowTypeError { get { if (_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 == Null.Instance || argArray == Undefined.Instance) { return func.Call(thisArg, Arguments.Empty); } var argArrayObj = argArray.TryCast(); 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()); } } }