using Jint.Native; using Jint.Runtime.Environments; using Jint.Runtime.Interpreter.Expressions; using Environment = Jint.Runtime.Environments.Environment; namespace Jint.Runtime.Interpreter.Statements { internal sealed class JintSwitchBlock { private readonly NodeList _switchBlock; private JintSwitchCase[] _jintSwitchBlock = Array.Empty(); private bool _initialized; public JintSwitchBlock(NodeList switchBlock) { _switchBlock = switchBlock; } private void Initialize() { _jintSwitchBlock = new JintSwitchCase[_switchBlock.Count]; for (var i = 0; i < _jintSwitchBlock.Length; i++) { _jintSwitchBlock[i] = new JintSwitchCase(_switchBlock[i]); } } public Completion Execute(EvaluationContext context, JsValue input) { if (!_initialized) { Initialize(); _initialized = true; } var v = JsValue.Undefined; var l = context.LastSyntaxElement; var hit = false; var defaultCaseIndex = -1; var i = 0; Environment? oldEnv = null; var temp = _jintSwitchBlock; DeclarativeEnvironment? blockEnv = null; start: for (; i < temp.Length; i++) { var clause = temp[i]; if (clause.LexicalDeclarations is not null && oldEnv is null) { oldEnv = context.Engine.ExecutionContext.LexicalEnvironment; blockEnv ??= JintEnvironment.NewDeclarativeEnvironment(context.Engine, oldEnv); blockEnv.Clear(); JintStatementList.BlockDeclarationInstantiation(blockEnv, clause.LexicalDeclarations); context.Engine.UpdateLexicalEnvironment(blockEnv); } if (clause.Test == null) { defaultCaseIndex = i; if (!hit) { continue; } } var clauseSelector = clause.Test?.GetValue(context); if (clauseSelector == input) { hit = true; } if (!hit) { if (oldEnv is not null) { context.Engine.UpdateLexicalEnvironment(oldEnv); oldEnv = null; } continue; } var r = clause.Consequent.Execute(context); if (r.Type != CompletionType.Normal) { if (oldEnv is not null) { context.Engine.UpdateLexicalEnvironment(oldEnv); } return r.UpdateEmpty(v); } l = r._source; v = r.Value.IsUndefined() ? v : r.Value; } // do we need to execute the default case ? if (!hit && defaultCaseIndex != -1) { // jump back to loop and start from default case hit = true; i = defaultCaseIndex; goto start; } if (oldEnv is not null) { context.Engine.UpdateLexicalEnvironment(oldEnv); } return new Completion(CompletionType.Normal, v, l); } private sealed class JintSwitchCase { internal readonly JintStatementList Consequent; internal readonly JintExpression? Test; internal readonly List? LexicalDeclarations; public JintSwitchCase(SwitchCase switchCase) { Consequent = new JintStatementList(statement: null, switchCase.Consequent); LexicalDeclarations = HoistingScope.GetLexicalDeclarations(switchCase); if (switchCase.Test != null) { Test = JintExpression.Build(switchCase.Test); } } } } }