JintSwitchBlock.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. using Jint.Native;
  2. using Jint.Runtime.Environments;
  3. using Jint.Runtime.Interpreter.Expressions;
  4. using Environment = Jint.Runtime.Environments.Environment;
  5. namespace Jint.Runtime.Interpreter.Statements;
  6. internal sealed class JintSwitchBlock
  7. {
  8. private readonly NodeList<SwitchCase> _switchBlock;
  9. private JintSwitchCase[] _jintSwitchBlock = [];
  10. private bool _initialized;
  11. public JintSwitchBlock(NodeList<SwitchCase> switchBlock)
  12. {
  13. _switchBlock = switchBlock;
  14. }
  15. private void Initialize()
  16. {
  17. _jintSwitchBlock = new JintSwitchCase[_switchBlock.Count];
  18. for (var i = 0; i < _jintSwitchBlock.Length; i++)
  19. {
  20. _jintSwitchBlock[i] = new JintSwitchCase(_switchBlock[i]);
  21. }
  22. }
  23. public Completion Execute(EvaluationContext context, JsValue input)
  24. {
  25. if (!_initialized)
  26. {
  27. Initialize();
  28. _initialized = true;
  29. }
  30. var v = JsValue.Undefined;
  31. var l = context.LastSyntaxElement;
  32. var hit = false;
  33. var defaultCaseIndex = -1;
  34. var i = 0;
  35. Environment? oldEnv = null;
  36. var temp = _jintSwitchBlock;
  37. DeclarativeEnvironment? blockEnv = null;
  38. start:
  39. for (; i < temp.Length; i++)
  40. {
  41. var clause = temp[i];
  42. if (clause.LexicalDeclarations.Declarations.Count > 0 && oldEnv is null)
  43. {
  44. oldEnv = context.Engine.ExecutionContext.LexicalEnvironment;
  45. blockEnv ??= JintEnvironment.NewDeclarativeEnvironment(context.Engine, oldEnv);
  46. blockEnv.Clear();
  47. JintStatementList.BlockDeclarationInstantiation(blockEnv, clause.LexicalDeclarations);
  48. context.Engine.UpdateLexicalEnvironment(blockEnv);
  49. }
  50. if (clause.Test == null)
  51. {
  52. defaultCaseIndex = i;
  53. if (!hit)
  54. {
  55. continue;
  56. }
  57. }
  58. var clauseSelector = clause.Test?.GetValue(context);
  59. if (clauseSelector == input)
  60. {
  61. hit = true;
  62. }
  63. if (!hit)
  64. {
  65. if (oldEnv is not null)
  66. {
  67. context.Engine.UpdateLexicalEnvironment(oldEnv);
  68. oldEnv = null;
  69. }
  70. continue;
  71. }
  72. var r = clause.Consequent.Execute(context);
  73. if (r.Type != CompletionType.Normal)
  74. {
  75. if (oldEnv is not null)
  76. {
  77. context.Engine.UpdateLexicalEnvironment(oldEnv);
  78. }
  79. return r.UpdateEmpty(v);
  80. }
  81. l = r._source;
  82. v = r.Value.IsUndefined() ? v : r.Value;
  83. }
  84. // do we need to execute the default case ?
  85. if (!hit && defaultCaseIndex != -1)
  86. {
  87. // jump back to loop and start from default case
  88. hit = true;
  89. i = defaultCaseIndex;
  90. goto start;
  91. }
  92. if (oldEnv is not null)
  93. {
  94. context.Engine.UpdateLexicalEnvironment(oldEnv);
  95. }
  96. return new Completion(CompletionType.Normal, v, l);
  97. }
  98. private sealed class JintSwitchCase
  99. {
  100. internal readonly JintStatementList Consequent;
  101. internal readonly JintExpression? Test;
  102. internal readonly DeclarationCache LexicalDeclarations;
  103. public JintSwitchCase(SwitchCase switchCase)
  104. {
  105. Consequent = new JintStatementList(statement: null, switchCase.Consequent);
  106. LexicalDeclarations = DeclarationCacheBuilder.Build(switchCase);
  107. if (switchCase.Test != null)
  108. {
  109. Test = JintExpression.Build(switchCase.Test);
  110. }
  111. }
  112. }
  113. }