using System.Collections.Generic; using Esprima.Ast; using Jint.Native; using Jint.Runtime.Environments; using Jint.Runtime.Interpreter.Expressions; namespace Jint.Runtime.Interpreter.Statements { /// /// https://tc39.es/ecma262/#sec-forbodyevaluation /// internal sealed class JintForStatement : JintStatement { private JintVariableDeclaration _initStatement; private JintExpression _initExpression; private JintExpression _test; private JintExpression _increment; private JintStatement _body; private List _boundNames; private bool _shouldCreatePerIterationEnvironment; public JintForStatement(Engine engine, ForStatement statement) : base(engine, statement) { _initialized = false; } protected override void Initialize() { _body = Build(_engine, _statement.Body); if (_statement.Init != null) { if (_statement.Init.Type == Nodes.VariableDeclaration) { var d = (VariableDeclaration) _statement.Init; if (d.Kind != VariableDeclarationKind.Var) { _boundNames = new List(); d.GetBoundNames(_boundNames); } _initStatement = new JintVariableDeclaration(_engine, d); _shouldCreatePerIterationEnvironment = d.Kind == VariableDeclarationKind.Let; } else { _initExpression = JintExpression.Build(_engine, (Expression) _statement.Init); } } if (_statement.Test != null) { _test = JintExpression.Build(_engine, _statement.Test); } if (_statement.Update != null) { _increment = JintExpression.Build(_engine, _statement.Update); } } protected override Completion ExecuteInternal() { EnvironmentRecord oldEnv = null; EnvironmentRecord loopEnv = null; if (_boundNames != null) { oldEnv = _engine.ExecutionContext.LexicalEnvironment; loopEnv = JintEnvironment.NewDeclarativeEnvironment(_engine, oldEnv); var loopEnvRec = loopEnv; var kind = _initStatement._statement.Kind; for (var i = 0; i < _boundNames.Count; i++) { var name = _boundNames[i]; if (kind == VariableDeclarationKind.Const) { loopEnvRec.CreateImmutableBinding(name, true); } else { loopEnvRec.CreateMutableBinding(name, false); } } _engine.UpdateLexicalEnvironment(loopEnv); } try { if (_initExpression != null) { _initExpression?.GetValue(); } else { _initStatement?.Execute(); } return ForBodyEvaluation(); } finally { if (oldEnv is not null) { _engine.UpdateLexicalEnvironment(oldEnv); } } } /// /// https://tc39.es/ecma262/#sec-forbodyevaluation /// private Completion ForBodyEvaluation() { var v = Undefined.Instance; if (_shouldCreatePerIterationEnvironment) { CreatePerIterationEnvironment(); } while (true) { if (_test != null) { if (!TypeConverter.ToBoolean(_test.GetValue())) { return new Completion(CompletionType.Normal, v, null, Location); } } var result = _body.Execute(); if (!ReferenceEquals(result.Value, null)) { v = result.Value; } if (result.Type == CompletionType.Break && (result.Identifier == null || result.Identifier == _statement?.LabelSet?.Name)) { return new Completion(CompletionType.Normal, result.Value, null, Location); } if (result.Type != CompletionType.Continue || (result.Identifier != null && result.Identifier != _statement?.LabelSet?.Name)) { if (result.Type != CompletionType.Normal) { return result; } } if (_shouldCreatePerIterationEnvironment) { CreatePerIterationEnvironment(); } _increment?.GetValue(); } } private void CreatePerIterationEnvironment() { if (_boundNames == null || _boundNames.Count == 0) { return; } var lastIterationEnv = _engine.ExecutionContext.LexicalEnvironment; var lastIterationEnvRec = lastIterationEnv; var outer = lastIterationEnv._outerEnv; var thisIterationEnv = JintEnvironment.NewDeclarativeEnvironment(_engine, outer); var thisIterationEnvRec = (DeclarativeEnvironmentRecord) thisIterationEnv; for (var j = 0; j < _boundNames.Count; j++) { var bn = _boundNames[j]; var lastValue = lastIterationEnvRec.GetBindingValue(bn, true); thisIterationEnvRec.CreateMutableBindingAndInitialize(bn, false, lastValue); } _engine.UpdateLexicalEnvironment(thisIterationEnv); } } }