123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- using System.Diagnostics.CodeAnalysis;
- using Jint.Native;
- using Jint.Native.Iterator;
- using Jint.Runtime.Environments;
- using Jint.Runtime.Interpreter.Expressions;
- using Environment = Jint.Runtime.Environments.Environment;
- namespace Jint.Runtime.Interpreter.Statements
- {
- /// <summary>
- /// https://tc39.es/ecma262/#sec-for-in-and-for-of-statements
- /// </summary>
- internal sealed class JintForInForOfStatement : JintStatement<Statement>
- {
- private readonly Node _leftNode;
- private readonly Statement _forBody;
- private readonly Expression _rightExpression;
- private readonly IterationKind _iterationKind;
- private ProbablyBlockStatement _body;
- private JintExpression? _expr;
- private DestructuringPattern? _assignmentPattern;
- private JintExpression _right = null!;
- private List<Key>? _tdzNames;
- private bool _destructuring;
- private LhsKind _lhsKind;
- public JintForInForOfStatement(ForInStatement statement) : base(statement)
- {
- _leftNode = statement.Left;
- _rightExpression = statement.Right;
- _forBody = statement.Body;
- _iterationKind = IterationKind.Enumerate;
- }
- public JintForInForOfStatement(ForOfStatement statement) : base(statement)
- {
- _leftNode = statement.Left;
- _rightExpression = statement.Right;
- _forBody = statement.Body;
- _iterationKind = IterationKind.Iterate;
- }
- protected override void Initialize(EvaluationContext context)
- {
- _lhsKind = LhsKind.Assignment;
- var engine = context.Engine;
- if (_leftNode is VariableDeclaration variableDeclaration)
- {
- _lhsKind = variableDeclaration.Kind == VariableDeclarationKind.Var
- ? LhsKind.VarBinding
- : LhsKind.LexicalBinding;
- var variableDeclarationDeclaration = variableDeclaration.Declarations[0];
- var id = variableDeclarationDeclaration.Id;
- if (_lhsKind == LhsKind.LexicalBinding)
- {
- _tdzNames = new List<Key>(1);
- id.GetBoundNames(_tdzNames);
- }
- if (id is DestructuringPattern pattern)
- {
- _destructuring = true;
- _assignmentPattern = pattern;
- }
- else
- {
- var identifier = (Identifier) id;
- _expr = new JintIdentifierExpression(identifier);
- }
- }
- else if (_leftNode is DestructuringPattern pattern)
- {
- _destructuring = true;
- _assignmentPattern = pattern;
- }
- else if (_leftNode is MemberExpression memberExpression)
- {
- _expr = new JintMemberExpression(memberExpression);
- }
- else
- {
- _expr = new JintIdentifierExpression((Identifier) _leftNode);
- }
- _body = new ProbablyBlockStatement(_forBody);
- _right = JintExpression.Build(_rightExpression);
- }
- protected override Completion ExecuteInternal(EvaluationContext context)
- {
- if (!HeadEvaluation(context, out var keyResult))
- {
- return new Completion(CompletionType.Normal, JsValue.Undefined, _statement);
- }
- return BodyEvaluation(context, _expr, _body, keyResult, IterationKind.Enumerate, _lhsKind);
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-runtime-semantics-forin-div-ofheadevaluation-tdznames-expr-iterationkind
- /// </summary>
- private bool HeadEvaluation(EvaluationContext context, [NotNullWhen(true)] out IteratorInstance? result)
- {
- var engine = context.Engine;
- var oldEnv = engine.ExecutionContext.LexicalEnvironment;
- var tdz = JintEnvironment.NewDeclarativeEnvironment(engine, oldEnv);
- if (_tdzNames != null)
- {
- var TDZEnvRec = tdz;
- foreach (var name in _tdzNames)
- {
- TDZEnvRec.CreateMutableBinding(name);
- }
- }
- engine.UpdateLexicalEnvironment(tdz);
- var exprValue = _right.GetValue(context);
- engine.UpdateLexicalEnvironment(oldEnv);
- if (_iterationKind == IterationKind.Enumerate)
- {
- if (exprValue.IsNullOrUndefined())
- {
- result = null;
- return false;
- }
- var obj = TypeConverter.ToObject(engine.Realm, exprValue);
- result = new IteratorInstance.EnumerableIterator(engine, obj.GetKeys());
- }
- else
- {
- result = exprValue as IteratorInstance ?? exprValue.GetIterator(engine.Realm);
- }
- return true;
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset
- /// </summary>
- private Completion BodyEvaluation(
- EvaluationContext context,
- JintExpression? lhs,
- in ProbablyBlockStatement stmt,
- IteratorInstance iteratorRecord,
- IterationKind iterationKind,
- LhsKind lhsKind,
- IteratorKind iteratorKind = IteratorKind.Sync)
- {
- var engine = context.Engine;
- var oldEnv = engine.ExecutionContext.LexicalEnvironment;
- var v = JsValue.Undefined;
- var destructuring = _destructuring;
- string? lhsName = null;
- var completionType = CompletionType.Normal;
- var close = false;
- try
- {
- while (true)
- {
- Environment? iterationEnv = null;
- if (!iteratorRecord.TryIteratorStep(out var nextResult))
- {
- close = true;
- return new Completion(CompletionType.Normal, v, _statement!);
- }
- if (iteratorKind == IteratorKind.Async)
- {
- // nextResult = await nextResult;
- ExceptionHelper.ThrowNotImplementedException("await");
- }
- var nextValue = nextResult.Get(CommonProperties.Value);
- close = true;
- object lhsRef = null!;
- if (lhsKind != LhsKind.LexicalBinding)
- {
- if (!destructuring)
- {
- lhsRef = lhs!.Evaluate(context);
- }
- }
- else
- {
- iterationEnv = JintEnvironment.NewDeclarativeEnvironment(engine, oldEnv);
- if (_tdzNames != null)
- {
- BindingInstantiation(iterationEnv);
- }
- engine.UpdateLexicalEnvironment(iterationEnv);
- if (!destructuring)
- {
- var identifier = (Identifier) ((VariableDeclaration) _leftNode).Declarations[0].Id;
- lhsName ??= identifier.Name;
- lhsRef = engine.ResolveBinding(lhsName);
- }
- }
- if (context.DebugMode)
- {
- context.Engine.Debugger.OnStep(_leftNode);
- }
- var status = CompletionType.Normal;
- if (!destructuring)
- {
- if (context.IsAbrupt())
- {
- close = true;
- status = context.Completion;
- }
- else
- {
- var reference = (Reference) lhsRef;
- if (lhsKind == LhsKind.LexicalBinding || _leftNode.Type == NodeType.Identifier && !reference.IsUnresolvableReference)
- {
- reference.InitializeReferencedBinding(nextValue);
- }
- else
- {
- engine.PutValue(reference, nextValue);
- }
- }
- }
- else
- {
- nextValue = DestructuringPatternAssignmentExpression.ProcessPatterns(
- context,
- _assignmentPattern!,
- nextValue,
- iterationEnv,
- checkPatternPropertyReference: _lhsKind != LhsKind.VarBinding);
- status = context.Completion;
- if (lhsKind == LhsKind.Assignment)
- {
- // DestructuringAssignmentEvaluation of assignmentPattern using nextValue as the argument.
- }
- #pragma warning disable MA0140
- else if (lhsKind == LhsKind.VarBinding)
- {
- // BindingInitialization for lhs passing nextValue and undefined as the arguments.
- }
- else
- {
- // BindingInitialization for lhs passing nextValue and iterationEnv as arguments
- }
- #pragma warning restore MA0140
- }
- if (status != CompletionType.Normal)
- {
- engine.UpdateLexicalEnvironment(oldEnv);
- if (_iterationKind == IterationKind.AsyncIterate)
- {
- iteratorRecord.Close(status);
- return new Completion(status, nextValue, context.LastSyntaxElement);
- }
- if (iterationKind == IterationKind.Enumerate)
- {
- return new Completion(status, nextValue, context.LastSyntaxElement);
- }
- iteratorRecord.Close(status);
- return new Completion(status, nextValue, context.LastSyntaxElement);
- }
- var result = stmt.Execute(context);
- engine.UpdateLexicalEnvironment(oldEnv);
- if (!result.Value.IsEmpty)
- {
- v = result.Value;
- }
- if (result.Type == CompletionType.Break && (context.Target == null || string.Equals(context.Target, _statement?.LabelSet?.Name, StringComparison.Ordinal)))
- {
- completionType = CompletionType.Normal;
- return new Completion(CompletionType.Normal, v, _statement!);
- }
- if (result.Type != CompletionType.Continue || (context.Target != null && !string.Equals(context.Target, _statement?.LabelSet?.Name, StringComparison.Ordinal)))
- {
- completionType = result.Type;
- if (iterationKind == IterationKind.Enumerate)
- {
- // TODO es6-generators make sure we can start from where we left off
- //return result;
- }
- if (result.IsAbrupt())
- {
- close = true;
- return result;
- }
- }
- }
- }
- catch
- {
- completionType = CompletionType.Throw;
- throw;
- }
- finally
- {
- if (close)
- {
- try
- {
- iteratorRecord.Close(completionType);
- }
- catch
- {
- // if we already have and exception, use it
- if (completionType != CompletionType.Throw)
- {
- #pragma warning disable CA2219
- #pragma warning disable MA0072
- throw;
- #pragma warning restore MA0072
- #pragma warning restore CA2219
- }
- }
- }
- engine.UpdateLexicalEnvironment(oldEnv);
- }
- }
- private void BindingInstantiation(Environment environment)
- {
- var envRec = (DeclarativeEnvironment) environment;
- var variableDeclaration = (VariableDeclaration) _leftNode;
- var boundNames = new List<Key>();
- variableDeclaration.GetBoundNames(boundNames);
- for (var i = 0; i < boundNames.Count; i++)
- {
- var name = boundNames[i];
- if (variableDeclaration.Kind == VariableDeclarationKind.Const)
- {
- envRec.CreateImmutableBinding(name, strict: true);
- }
- else
- {
- envRec.CreateMutableBinding(name, canBeDeleted: false);
- }
- }
- }
- private enum LhsKind
- {
- Assignment,
- VarBinding,
- LexicalBinding
- }
- private enum IteratorKind
- {
- Sync,
- Async
- }
- private enum IterationKind
- {
- Enumerate,
- Iterate,
- AsyncIterate
- }
- }
- }
|