using Esprima.Ast; using Jint.Native; using Jint.Native.Iterator; using Jint.Runtime.Interpreter.Expressions; using Jint.Runtime.References; namespace Jint.Runtime.Interpreter.Statements { /// /// https://www.ecma-international.org/ecma-262/6.0/#sec-for-in-and-for-of-statements /// internal sealed class JintForOfStatement : JintStatement { private JintStatement _body; private JintExpression _identifier; private BindingPattern _leftPattern; private JintExpression _right; public JintForOfStatement(Engine engine, ForOfStatement statement) : base(engine, statement) { _initialized = false; } protected override void Initialize() { if (_statement.Left is VariableDeclaration variableDeclaration) { var element = variableDeclaration.Declarations[0].Id; if (element is BindingPattern bindingPattern) { _leftPattern = bindingPattern; } else { _identifier = JintExpression.Build(_engine, (Identifier) element); } } else if (_statement.Left is BindingPattern bindingPattern) { _leftPattern = bindingPattern; } else { _identifier = JintExpression.Build(_engine, (Expression) _statement.Left); } _body = Build(_engine, _statement.Body); _right = JintExpression.Build(_engine, _statement.Right); } protected override Completion ExecuteInternal() { var experValue = _right.GetValue(); if (!(experValue is IIterator iterator)) { var obj = TypeConverter.ToObject(_engine, experValue); obj.TryGetIterator(_engine, out iterator); } if (iterator is null) { return ExceptionHelper.ThrowTypeError(_engine, _identifier + " is not iterable"); } var completionType = CompletionType.Normal; var v = JsValue.Undefined; var close = false; try { do { iterator.TryIteratorStep(out var item); var done = item.Get(CommonProperties.Done); if (TypeConverter.ToBoolean(done)) { // we can close after checks pass close = true; break; } var currentValue = item.Get(CommonProperties.Value); // we can close after checks pass close = true; if (_leftPattern != null) { BindingPatternAssignmentExpression.ProcessPatterns( _engine, _leftPattern, currentValue, checkReference: !(_statement.Left is VariableDeclaration)); } else { var varRef = (Reference) _identifier.Evaluate(); _engine.PutValue(varRef, currentValue); } var stmt = _body.Execute(); if (!ReferenceEquals(stmt.Value, null)) { v = stmt.Value; } if (stmt.Type == CompletionType.Break && (stmt.Identifier == null || stmt.Identifier == _statement?.LabelSet?.Name)) { return new Completion(CompletionType.Normal, stmt.Value, null, Location); } if (stmt.Type != CompletionType.Continue || ((stmt.Identifier != null) && stmt.Identifier != _statement?.LabelSet?.Name)) { if (stmt.Type != CompletionType.Normal) { return stmt; } } } while (true); } catch { completionType = CompletionType.Throw; throw; } finally { if (close) { iterator.Close(completionType); } } return new Completion(CompletionType.Normal, v, null, Location); } } }