JintForStatement.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. using Esprima.Ast;
  2. using Jint.Native;
  3. using Jint.Runtime.Environments;
  4. using Jint.Runtime.Interpreter.Expressions;
  5. namespace Jint.Runtime.Interpreter.Statements
  6. {
  7. /// <summary>
  8. /// https://tc39.es/ecma262/#sec-forbodyevaluation
  9. /// </summary>
  10. internal sealed class JintForStatement : JintStatement<ForStatement>
  11. {
  12. private JintVariableDeclaration? _initStatement;
  13. private JintExpression? _initExpression;
  14. private JintExpression? _test;
  15. private JintExpression? _increment;
  16. private ProbablyBlockStatement _body;
  17. private List<string>? _boundNames;
  18. private bool _shouldCreatePerIterationEnvironment;
  19. public JintForStatement(ForStatement statement) : base(statement)
  20. {
  21. }
  22. protected override void Initialize(EvaluationContext context)
  23. {
  24. var engine = context.Engine;
  25. _body = new ProbablyBlockStatement(_statement.Body);
  26. if (_statement.Init != null)
  27. {
  28. if (_statement.Init.Type == Nodes.VariableDeclaration)
  29. {
  30. var d = (VariableDeclaration) _statement.Init;
  31. if (d.Kind != VariableDeclarationKind.Var)
  32. {
  33. _boundNames = new List<string>();
  34. d.GetBoundNames(_boundNames);
  35. }
  36. _initStatement = new JintVariableDeclaration(d);
  37. _shouldCreatePerIterationEnvironment = d.Kind == VariableDeclarationKind.Let;
  38. }
  39. else
  40. {
  41. _initExpression = JintExpression.Build(engine, (Expression) _statement.Init);
  42. }
  43. }
  44. if (_statement.Test != null)
  45. {
  46. _test = JintExpression.Build(engine, _statement.Test);
  47. }
  48. if (_statement.Update != null)
  49. {
  50. _increment = JintExpression.Build(engine, _statement.Update);
  51. }
  52. }
  53. protected override Completion ExecuteInternal(EvaluationContext context)
  54. {
  55. EnvironmentRecord? oldEnv = null;
  56. EnvironmentRecord? loopEnv = null;
  57. var engine = context.Engine;
  58. if (_boundNames != null)
  59. {
  60. oldEnv = engine.ExecutionContext.LexicalEnvironment;
  61. loopEnv = JintEnvironment.NewDeclarativeEnvironment(engine, oldEnv);
  62. var loopEnvRec = loopEnv;
  63. var kind = _initStatement!._statement.Kind;
  64. for (var i = 0; i < _boundNames.Count; i++)
  65. {
  66. var name = _boundNames[i];
  67. if (kind == VariableDeclarationKind.Const)
  68. {
  69. loopEnvRec.CreateImmutableBinding(name, true);
  70. }
  71. else
  72. {
  73. loopEnvRec.CreateMutableBinding(name, false);
  74. }
  75. }
  76. engine.UpdateLexicalEnvironment(loopEnv);
  77. }
  78. try
  79. {
  80. if (_initExpression != null)
  81. {
  82. _initExpression?.GetValue(context);
  83. }
  84. else
  85. {
  86. _initStatement?.Execute(context);
  87. }
  88. return ForBodyEvaluation(context);
  89. }
  90. finally
  91. {
  92. if (oldEnv is not null)
  93. {
  94. engine.UpdateLexicalEnvironment(oldEnv);
  95. }
  96. }
  97. }
  98. /// <summary>
  99. /// https://tc39.es/ecma262/#sec-forbodyevaluation
  100. /// </summary>
  101. private Completion ForBodyEvaluation(EvaluationContext context)
  102. {
  103. var v = Undefined.Instance;
  104. if (_shouldCreatePerIterationEnvironment)
  105. {
  106. CreatePerIterationEnvironment(context);
  107. }
  108. var debugHandler = context.DebugMode ? context.Engine.DebugHandler : null;
  109. while (true)
  110. {
  111. if (_test != null)
  112. {
  113. debugHandler?.OnStep(_test._expression);
  114. if (!TypeConverter.ToBoolean(_test.GetValue(context).Value))
  115. {
  116. return new Completion(CompletionType.Normal, v, ((JintStatement) this)._statement);
  117. }
  118. }
  119. var result = _body.Execute(context);
  120. if (!ReferenceEquals(result.Value, null))
  121. {
  122. v = result.Value;
  123. }
  124. if (result.Type == CompletionType.Break && (result.Target == null || result.Target == _statement?.LabelSet?.Name))
  125. {
  126. return new Completion(CompletionType.Normal, result.Value!, ((JintStatement) this)._statement);
  127. }
  128. if (result.Type != CompletionType.Continue || (result.Target != null && result.Target != _statement?.LabelSet?.Name))
  129. {
  130. if (result.Type != CompletionType.Normal)
  131. {
  132. return result;
  133. }
  134. }
  135. if (_shouldCreatePerIterationEnvironment)
  136. {
  137. CreatePerIterationEnvironment(context);
  138. }
  139. if (_increment != null)
  140. {
  141. debugHandler?.OnStep(_increment._expression);
  142. _increment.Evaluate(context);
  143. }
  144. }
  145. }
  146. private void CreatePerIterationEnvironment(EvaluationContext context)
  147. {
  148. var engine = context.Engine;
  149. var lastIterationEnv = engine.ExecutionContext.LexicalEnvironment;
  150. var lastIterationEnvRec = lastIterationEnv;
  151. var outer = lastIterationEnv._outerEnv;
  152. var thisIterationEnv = JintEnvironment.NewDeclarativeEnvironment(engine, outer);
  153. for (var j = 0; j < _boundNames!.Count; j++)
  154. {
  155. var bn = _boundNames[j];
  156. var lastValue = lastIterationEnvRec.GetBindingValue(bn, true);
  157. thisIterationEnv.CreateMutableBindingAndInitialize(bn, false, lastValue);
  158. }
  159. engine.UpdateLexicalEnvironment(thisIterationEnv);
  160. }
  161. }
  162. }