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 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; }
public override JsValue Call(JsValue thisObject, JsValue[] arguments)
{
return Construct(arguments, thisObject);
}
///
/// https://tc39.es/ecma262/#sec-createdynamicfunction
///
public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
{
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 = null;
try
{
string functionExpression;
if (argCount == 0)
{
functionExpression = "function f(){}";
}
else
{
functionExpression = "function f(";
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)
{
ExceptionHelper.ThrowSyntaxError(_realm);
}
// TODO generators etc, rewrite logic
var proto = GetPrototypeFromConstructor(newTarget, static intrinsics => intrinsics.Function.PrototypeObject);
var functionObject = new ScriptFunctionInstance(
Engine,
function,
_realm.GlobalEnv,
function.Strict,
proto)
{
_realm = _realm
};
functionObject.MakeConstructor();
// the function is not actually a named function
functionObject.SetFunctionName(_functionNameAnonymous, force: true);
return functionObject;
}
///
/// https://tc39.es/ecma262/#sec-runtime-semantics-instantiatefunctionobject
///
internal FunctionInstance InstantiateFunctionObject(FunctionDeclaration functionDeclaration, EnvironmentRecord env)
{
var functionObject = new ScriptFunctionInstance(
Engine,
functionDeclaration,
env,
functionDeclaration.Strict || _engine._isStrict)
{
_realm = _realm
};
functionObject.MakeConstructor();
return functionObject;
}
}
}