using Jint.Native;
using Jint.Runtime.Environments;
namespace Jint.Runtime.Interpreter.Statements
{
///
/// https://tc39.es/ecma262/#sec-try-statement
///
internal sealed class JintTryStatement : JintStatement
{
private JintBlockStatement _block = null!;
private JintBlockStatement? _catch;
private JintBlockStatement? _finalizer;
public JintTryStatement(TryStatement statement) : base(statement)
{
}
protected override void Initialize(EvaluationContext context)
{
_block = new JintBlockStatement(_statement.Block);
if (_statement.Finalizer != null)
{
_finalizer = new JintBlockStatement(_statement.Finalizer);
}
}
protected override Completion ExecuteInternal(EvaluationContext context)
{
var engine = context.Engine;
var b = _block.Execute(context);
if (b.Type == CompletionType.Throw)
{
b = ExecuteCatch(context, b, engine);
}
if (_finalizer != null)
{
var f = _finalizer.Execute(context);
if (f.Type == CompletionType.Normal)
{
return b;
}
return f.UpdateEmpty(JsValue.Undefined);
}
return b.UpdateEmpty(JsValue.Undefined);
}
private Completion ExecuteCatch(EvaluationContext context, Completion b, Engine engine)
{
// execute catch
if (_statement.Handler is not null)
{
// initialize lazily
if (_catch is null)
{
_catch = new JintBlockStatement(_statement.Handler.Body);
}
// https://tc39.es/ecma262/#sec-runtime-semantics-catchclauseevaluation
var thrownValue = b.Value;
var oldEnv = engine.ExecutionContext.LexicalEnvironment;
var catchEnv = JintEnvironment.NewDeclarativeEnvironment(engine, oldEnv, catchEnvironment: true);
var boundNames = new List();
_statement.Handler.Param.GetBoundNames(boundNames);
for (var i = 0; i < boundNames.Count; i++)
{
catchEnv.CreateMutableBinding(boundNames[i]);
}
engine.UpdateLexicalEnvironment(catchEnv);
var catchParam = _statement.Handler?.Param;
catchParam.BindingInitialization(context, thrownValue, catchEnv);
b = _catch.Execute(context);
engine.UpdateLexicalEnvironment(oldEnv);
}
return b;
}
}
}