using System; using Esprima; using Esprima.Ast; using Jint.Native.Object; using Jint.Runtime; using Jint.Runtime.Descriptors; using Jint.Runtime.Environments; using Jint.Runtime.Interpreter; namespace Jint.Native.Function { /// /// https://tc39.es/ecma262/#sec-function-constructor /// public sealed class FunctionConstructor : FunctionInstance, IConstructor { private static readonly ParserOptions ParserOptions = new ParserOptions { Tolerant = false }; private static readonly JsString _functionName = new JsString("Function"); private static readonly JsString _functionNameAnonymous = new JsString("anonymous"); internal FunctionConstructor( Engine engine, Realm realm, ObjectPrototype objectPrototype) : base(engine, realm, _functionName) { PrototypeObject = new FunctionPrototype(engine, realm, objectPrototype); _prototype = PrototypeObject; _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden); _length = new PropertyDescriptor(JsNumber.PositiveOne, PropertyFlag.Configurable); } public FunctionPrototype PrototypeObject { get; } protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments) { return Construct(arguments, thisObject); } ObjectInstance IConstructor.Construct(JsValue[] arguments, JsValue newTarget) => Construct(arguments, newTarget); private ObjectInstance Construct(JsValue[] arguments, JsValue newTarget) { var function = CreateDynamicFunction( this, newTarget, FunctionKind.Normal, arguments); return function; } /// /// https://tc39.es/ecma262/#sec-createdynamicfunction /// internal FunctionInstance CreateDynamicFunction( ObjectInstance constructor, JsValue newTarget, FunctionKind kind, JsValue[] args) { // TODO var callerContext = _engine.GetExecutionContext(1); var callerContext = _engine.ExecutionContext; var callerRealm = callerContext.Realm; var calleeRealm = _engine.ExecutionContext.Realm; _engine._host.EnsureCanCompileStrings(callerRealm, calleeRealm); if (newTarget.IsUndefined()) { newTarget = constructor; } Func fallbackProto = null; switch (kind) { case FunctionKind.Normal: fallbackProto = static intrinsics => intrinsics.Function.PrototypeObject; break; case FunctionKind.Generator: case FunctionKind.AsyncGenerator: case FunctionKind.Async: default: ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(kind), kind.ToString()); break; } var argCount = args.Length; var p = ""; var body = ""; if (argCount == 1) { body = TypeConverter.ToString(args[0]); } else if (argCount > 1) { var firstArg = args[0]; p = TypeConverter.ToString(firstArg); for (var k = 1; k < argCount - 1; k++) { var nextArg = args[k]; p += "," + TypeConverter.ToString(nextArg); } body = TypeConverter.ToString(args[argCount - 1]); } IFunction function = null; try { string functionExpression = null; if (argCount == 0) { switch (kind) { case FunctionKind.Normal: functionExpression = "function f(){}"; break; case FunctionKind.Generator: functionExpression = "function* f(){}"; break; case FunctionKind.Async: ExceptionHelper.ThrowNotImplementedException("Async functions not implemented"); break; case FunctionKind.AsyncGenerator: ExceptionHelper.ThrowNotImplementedException("Async generators not implemented"); break; default: ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(kind), kind.ToString()); break; } } else { switch (kind) { case FunctionKind.Normal: functionExpression = "function f("; break; case FunctionKind.Generator: functionExpression = "function* f("; break; case FunctionKind.Async: ExceptionHelper.ThrowNotImplementedException("Async functions not implemented"); break; case FunctionKind.AsyncGenerator: ExceptionHelper.ThrowNotImplementedException("Async generators not implemented"); break; default: ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(kind), kind.ToString()); break; } if (p.IndexOf('/') != -1) { // ensure comments don't screw up things functionExpression += "\n" + p + "\n"; } else { functionExpression += p; } functionExpression += ")"; if (body.IndexOf('/') != -1) { // ensure comments don't screw up things functionExpression += "{\n" + body + "\n}"; } else { functionExpression += "{" + body + "}"; } } var parser = new JavaScriptParser(functionExpression, ParserOptions); function = (IFunction) parser.ParseScript().Body[0]; } catch (ParserException ex) { ExceptionHelper.ThrowSyntaxError(_engine.ExecutionContext.Realm, ex.Message); } var proto = GetPrototypeFromConstructor(newTarget, fallbackProto); var realmF = _realm; var scope = realmF.GlobalEnv; PrivateEnvironmentRecord privateScope = null; var definition = new JintFunctionDefinition(_engine, function); FunctionInstance F = OrdinaryFunctionCreate(proto, definition, function.Strict ? FunctionThisMode.Strict : FunctionThisMode.Global, scope, privateScope); F.SetFunctionName(_functionNameAnonymous, force: true); if (kind == FunctionKind.Generator) { ExceptionHelper.ThrowNotImplementedException("generators not implemented"); } else if (kind == FunctionKind.AsyncGenerator) { // TODO // Let prototype be ! OrdinaryObjectCreate(%AsyncGeneratorFunction.prototype.prototype%). // Perform DefinePropertyOrThrow(F, "prototype", PropertyDescriptor { [[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }). ExceptionHelper.ThrowNotImplementedException("async generators not implemented"); } else if (kind == FunctionKind.Normal) { F.MakeConstructor(); } return F; } /// /// https://tc39.es/ecma262/#sec-ordinaryfunctioncreate /// internal ScriptFunctionInstance OrdinaryFunctionCreate( ObjectInstance functionPrototype, JintFunctionDefinition function, FunctionThisMode thisMode, EnvironmentRecord scope, PrivateEnvironmentRecord privateScope) { return new ScriptFunctionInstance( _engine, function, scope, thisMode, functionPrototype) { _privateEnvironment = privateScope, _realm = _realm }; } /// /// https://tc39.es/ecma262/#sec-runtime-semantics-instantiatefunctionobject /// internal FunctionInstance InstantiateFunctionObject( JintFunctionDefinition functionDeclaration, EnvironmentRecord scope, PrivateEnvironmentRecord privateScope) { return !functionDeclaration.Function.Generator ? InstantiateOrdinaryFunctionObject(functionDeclaration, scope, privateScope) : InstantiateGeneratorFunctionObject(functionDeclaration, scope, privateScope); } /// /// https://tc39.es/ecma262/#sec-runtime-semantics-instantiateordinaryfunctionobject /// private FunctionInstance InstantiateOrdinaryFunctionObject( JintFunctionDefinition functionDeclaration, EnvironmentRecord scope, PrivateEnvironmentRecord privateScope) { var F = OrdinaryFunctionCreate( _realm.Intrinsics.Function.PrototypeObject, functionDeclaration, functionDeclaration.ThisMode, scope, privateScope); var name = functionDeclaration.Name ?? "default"; F.SetFunctionName(name); F.MakeConstructor(); return F; } /// /// https://tc39.es/ecma262/#sec-runtime-semantics-instantiategeneratorfunctionobject /// private FunctionInstance InstantiateGeneratorFunctionObject( JintFunctionDefinition functionDeclaration, EnvironmentRecord scope, PrivateEnvironmentRecord privateScope) { // TODO generators return InstantiateOrdinaryFunctionObject(functionDeclaration, scope, privateScope); } } }