using Jint.Native;
using Jint.Native.Function;
using Jint.Native.Object;
using Jint.Runtime.Descriptors;
using Jint.Runtime.Environments;
namespace Jint.Runtime.Interpreter.Expressions;
internal sealed class JintFunctionExpression : JintExpression
{
private readonly JintFunctionDefinition _function;
public JintFunctionExpression(FunctionExpression function) : base(function)
{
_function = new JintFunctionDefinition(function);
}
protected override object EvaluateInternal(EvaluationContext context)
{
return Build(context.Engine, _function);
}
public override JsValue GetValue(EvaluationContext context)
{
return Build(context.Engine, _function);
}
private static ScriptFunction Build(Engine engine, JintFunctionDefinition function)
{
ScriptFunction closure;
var functionName = function.Name ?? "";
if (!function.Function.Generator)
{
closure = function.Function.Async
? InstantiateAsyncFunctionExpression(engine, function, functionName)
: InstantiateOrdinaryFunctionExpression(engine, function, functionName);
}
else
{
closure = function.Function.Async
? InstantiateAsyncGeneratorFunctionExpression(engine, function, functionName)
: InstantiateGeneratorFunctionExpression(engine, function, functionName);
}
return closure;
}
///
/// https://tc39.es/ecma262/#sec-runtime-semantics-instantiateordinaryfunctionexpression
///
private static ScriptFunction InstantiateOrdinaryFunctionExpression(Engine engine, JintFunctionDefinition function, string? name = "")
{
var runningExecutionContext = engine.ExecutionContext;
var env = runningExecutionContext.LexicalEnvironment;
var privateEnv = runningExecutionContext.PrivateEnvironment;
DeclarativeEnvironment? funcEnv = null;
if (!string.IsNullOrWhiteSpace(name))
{
funcEnv = JintEnvironment.NewDeclarativeEnvironment(engine, engine.ExecutionContext.LexicalEnvironment);
funcEnv.CreateImmutableBinding(name!, strict: false);
}
var thisMode = function.Strict
? FunctionThisMode.Strict
: FunctionThisMode.Global;
var intrinsics = engine.Realm.Intrinsics;
var closure = intrinsics.Function.OrdinaryFunctionCreate(
intrinsics.Function.PrototypeObject,
function,
thisMode,
funcEnv ?? env,
privateEnv
);
if (name is not null)
{
closure.SetFunctionName(name);
}
closure.MakeConstructor();
funcEnv?.InitializeBinding(name!, closure);
return closure;
}
///
/// https://tc39.es/ecma262/#sec-runtime-semantics-instantiateasyncfunctionexpression
///
private static ScriptFunction InstantiateAsyncFunctionExpression(Engine engine, JintFunctionDefinition function, string? name = "")
{
var runningExecutionContext = engine.ExecutionContext;
var env = runningExecutionContext.LexicalEnvironment;
var privateEnv = runningExecutionContext.PrivateEnvironment;
DeclarativeEnvironment? funcEnv = null;
if (!string.IsNullOrWhiteSpace(name))
{
funcEnv = JintEnvironment.NewDeclarativeEnvironment(engine, engine.ExecutionContext.LexicalEnvironment);
funcEnv.CreateImmutableBinding(name!, strict: false);
}
var thisMode = function.Strict
? FunctionThisMode.Strict
: FunctionThisMode.Global;
var intrinsics = engine.Realm.Intrinsics;
var closure = intrinsics.Function.OrdinaryFunctionCreate(
intrinsics.AsyncFunction.PrototypeObject,
function,
thisMode,
funcEnv ?? env,
privateEnv
);
closure.SetFunctionName(name ?? "");
funcEnv?.InitializeBinding(name!, closure);
return closure;
}
///
/// https://tc39.es/ecma262/#sec-runtime-semantics-instantiategeneratorfunctionexpression
///
private static ScriptFunction InstantiateGeneratorFunctionExpression(Engine engine, JintFunctionDefinition function, string? name)
{
var runningExecutionContext = engine.ExecutionContext;
var env = runningExecutionContext.LexicalEnvironment;
var privateEnv = runningExecutionContext.PrivateEnvironment;
DeclarativeEnvironment? funcEnv = null;
if (!string.IsNullOrWhiteSpace(name))
{
funcEnv = JintEnvironment.NewDeclarativeEnvironment(engine, engine.ExecutionContext.LexicalEnvironment);
funcEnv.CreateImmutableBinding(name!, strict: false);
}
var thisMode = function.Strict || engine._isStrict
? FunctionThisMode.Strict
: FunctionThisMode.Global;
var intrinsics = engine.Realm.Intrinsics;
var closure = intrinsics.Function.OrdinaryFunctionCreate(
intrinsics.GeneratorFunction.PrototypeObject,
function,
thisMode,
funcEnv ?? env,
privateEnv
);
if (name is not null)
{
closure.SetFunctionName(name);
}
var prototype = ObjectInstance.OrdinaryObjectCreate(engine, intrinsics.GeneratorFunction.PrototypeObject.PrototypeObject);
closure.DefinePropertyOrThrow(CommonProperties.Prototype, new PropertyDescriptor(prototype, PropertyFlag.Writable));
funcEnv?.InitializeBinding(name!, closure);
return closure;
}
///
/// https://tc39.es/ecma262/#sec-runtime-semantics-instantiateasyncgeneratorfunctionexpression
///
private static ScriptFunction InstantiateAsyncGeneratorFunctionExpression(Engine engine, JintFunctionDefinition function, string? name)
{
var runningExecutionContext = engine.ExecutionContext;
var env = runningExecutionContext.LexicalEnvironment;
var privateEnv = runningExecutionContext.PrivateEnvironment;
DeclarativeEnvironment? funcEnv = null;
if (!string.IsNullOrWhiteSpace(name))
{
funcEnv = JintEnvironment.NewDeclarativeEnvironment(engine, engine.ExecutionContext.LexicalEnvironment);
funcEnv.CreateImmutableBinding(name!, strict: false);
}
var thisMode = function.Strict || engine._isStrict
? FunctionThisMode.Strict
: FunctionThisMode.Global;
var intrinsics = engine.Realm.Intrinsics;
var closure = intrinsics.Function.OrdinaryFunctionCreate(
intrinsics.AsyncGeneratorFunction.PrototypeObject,
function,
thisMode,
funcEnv ?? env,
privateEnv
);
if (name is not null)
{
closure.SetFunctionName(name);
}
var prototype = ObjectInstance.OrdinaryObjectCreate(engine, intrinsics.AsyncGeneratorFunction.PrototypeObject.PrototypeObject);
closure.DefinePropertyOrThrow(CommonProperties.Prototype, new PropertyDescriptor(prototype, PropertyFlag.Writable));
funcEnv?.InitializeBinding(name!, closure);
return closure;
}
}