private Tuple <List <JsStatement>, int> ProcessInner(JsBlockStatement statement, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, ImmutableStack <Tuple <int, string> > finallyStack, int?parentState)
        {
            var oldLoopLabel       = _currentLoopLabel;
            var oldRemainingBlocks = _remainingBlocks;

            try {
                _currentLoopLabel = _allocateLoopLabel();
                _remainingBlocks  = new Queue <RemainingBlock>();
                _remainingBlocks.Enqueue(new RemainingBlock(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(statement, 0)), breakStack, continueStack, CreateNewStateValue(finallyStack), new State(_currentLoopLabel, -1, finallyStack)));
                if (_exitState == null)
                {
                    _exitState = new State(_currentLoopLabel, -1, ImmutableStack <Tuple <int, string> > .Empty);
                }

                var sections = new List <Section>();

                int iterationCount = 0;

                while (_remainingBlocks.Count > 0)
                {
                    var current = _remainingBlocks.Dequeue();
                    var list    = Handle(current.Stack, current.BreakStack, current.ContinueStack, current.StateValue, current.ReturnState, _isIteratorBlock || _isAsync, true);
                    // Merge all top-level blocks that should be merged with their parents.
                    list = list.SelectMany(stmt => (stmt is JsBlockStatement && ((JsBlockStatement)stmt).MergeWithParent) ? ((JsBlockStatement)stmt).Statements : (IList <JsStatement>) new[] { stmt }).ToList();
                    sections.Add(new Section(current.StateValue, list));

                    if (iterationCount++ > 100000)
                    {
                        throw new Exception("Infinite loop when rewriting method to a state machine");
                    }
                }

                if (parentState != null && _isAsync)
                {
                    List <int> childStates;
                    if (!_childStates.TryGetValue(parentState.Value, out childStates))
                    {
                        _childStates[parentState.Value] = childStates = new List <int>();
                    }
                    childStates.AddRange(sections.Select(s => s.State.StateValue));
                }

                var body = new List <JsStatement> {
                    JsStatement.Label(_currentLoopLabel,
                                      JsStatement.For(JsStatement.Empty, null, null,
                                                      JsStatement.Switch(JsExpression.Identifier(_stateVariableName),
                                                                         sections.Select(b => JsStatement.SwitchSection(
                                                                                             GetAllContainedStateValues(b.State.StateValue).OrderBy(v => v).Select(v => JsExpression.Number(v)),
                                                                                             JsStatement.Block(b.Statements)))
                                                                         .Concat(new[] { JsStatement.SwitchSection(new JsExpression[] { null }, JsStatement.Break(_currentLoopLabel)) }))))
                };
                return(Tuple.Create(body, sections[0].State.StateValue));
            }
            finally {
                _currentLoopLabel = oldLoopLabel;
                _remainingBlocks  = oldRemainingBlocks;
            }
        }
 private static JsStatement GenerateBody(string stateVariableName, List <Node> nodes)
 {
     return(JsStatement.Switch(JsExpression.Identifier(stateVariableName),
                               nodes.Select(n => JsStatement.SwitchSection(n.StateValues.Select(v => JsExpression.Number(v)),
                                                                           JsStatement.Try(
                                                                               n.Children.Count > 0 ? (JsStatement)JsStatement.Block(GenerateBody(stateVariableName, n.Children), JsStatement.Break()) : JsStatement.Break(),
                                                                               null,
                                                                               JsExpression.Invocation(JsExpression.Member(JsExpression.Identifier(n.HandlerName), "call"), JsExpression.This))))));
 }
 public void SwitchStatementWorks()
 {
     AssertCorrect(JsStatement.Switch(JsExpression.Identifier("x"),
                                      JsStatement.SwitchSection(new[] { JsExpression.Number(0) }, JsExpression.Identifier("a")),
                                      JsStatement.SwitchSection(new[] { JsExpression.Number(1), JsExpression.Number(2) }, JsExpression.Identifier("b")),
                                      JsStatement.SwitchSection(new[] { null, JsExpression.Number(3) }, JsExpression.Identifier("c"))
                                      ),
                   "switch(x){case 0:{a;}case 1:case 2:{b;}default:case 3:{c;}}");
 }
        public override JsStatement VisitSwitchStatement(JsSwitchStatement statement, object data)
        {
            var oldBreak    = _breakStack;
            var oldContinue = _continueStack;

            try {
                _breakStack    = _breakStack.Push(null);
                _continueStack = _continueStack.Push(null);
                var sections = VisitSwitchSections(statement.Sections, data);
                return(ReferenceEquals(sections, statement.Sections) ? statement : JsStatement.Switch(statement.Expression, sections));
            }
            finally {
                _breakStack    = oldBreak;
                _continueStack = oldContinue;
            }
        }
        public virtual JsStatement VisitSwitchStatement(JsSwitchStatement statement, TData data)
        {
            var test    = VisitExpression(statement.Expression, data);
            var clauses = VisitSwitchSections(statement.Sections, data);

            return(ReferenceEquals(test, statement.Expression) && ReferenceEquals(clauses, statement.Sections) ? statement : JsStatement.Switch(test, clauses));
        }