예제 #1
0
        public JsStatement VisitGotoStateStatement(JsGotoStateStatement statement, object data)
        {
            var   result = new List <JsStatement>();
            State targetState;

            if (statement.TargetState == null)
            {
                if (!_labelStates.TryGetValue(statement.TargetLabel, out targetState))
                {
                    throw new InvalidOperationException("The label " + statement.TargetLabel + " does not exist.");
                }
            }
            else
            {
                targetState = statement.TargetState.Value;
            }

            var remaining = statement.CurrentState.FinallyStack;

            for (int i = 0, n = remaining.Count() - targetState.FinallyStack.Count(); i < n; i++)
            {
                var current = remaining.Peek();
                remaining = remaining.Pop();
                result.Add(JsExpression.Assign(JsExpression.Identifier(_stateVariableName), JsExpression.Number(remaining.IsEmpty ? -1 : remaining.Peek().Item1)));
                result.Add(JsExpression.Invocation(JsExpression.Member(JsExpression.Identifier(current.Item2), "call"), JsExpression.This));
            }

            result.Add(MakeSetNextStateStatement(targetState.StateValue));
            result.Add(targetState.StateValue == -1 ? (JsStatement)JsStatement.Break(targetState.LoopLabelName) : JsStatement.Continue(targetState.LoopLabelName));
            return(JsStatement.BlockMerged(result));
        }
예제 #2
0
        public override JsStatement VisitVariableDeclarationStatement(JsVariableDeclarationStatement statement, object data)
        {
            List <JsExpression> list = null;

            foreach (var d in statement.Declarations)
            {
                _variables.Add(d.Name);
                if (d.Initializer != null)
                {
                    list = list ?? new List <JsExpression>();
                    list.Add(JsExpression.Assign(JsExpression.Identifier(d.Name), d.Initializer));
                }
            }
            if (list == null)
            {
                return(JsStatement.BlockMerged(new JsStatement[0]));
            }
            else if (list.Count == 1)
            {
                return(list[0]);
            }
            else
            {
                return(JsExpression.Comma(list));
            }
        }
 public override JsStatement VisitReturnStatement(JsReturnStatement statement, object data)
 {
     if (_makeSetResult != null)
     {
         return(JsStatement.BlockMerged(_makeSetResult(statement.Value), JsStatement.Return()));
     }
     else
     {
         return(statement);
     }
 }
        private bool HandleTryStatement(JsTryStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock, bool isFirstStatement)
        {
            if (_isIteratorBlock && (FindInterestingConstructsVisitor.Analyze(stmt.GuardedStatement, InterestingConstruct.YieldReturn) || (stmt.Finally != null && stmt.Catch == null && !currentState.FinallyStack.IsEmpty)))
            {
                if (stmt.Catch != null)
                {
                    throw new InvalidOperationException("Cannot yield return from try with catch");
                }
                string           handlerName = _allocateFinallyHandler();
                JsBlockStatement handler;
                if (FindInterestingConstructsVisitor.Analyze(stmt.Finally, InterestingConstruct.Label))
                {
                    var inner = ProcessInner(stmt.Finally, breakStack, continueStack, currentState.FinallyStack, currentState.StateValue);
                    handler = JsStatement.Block(new[]  { new JsSetNextStateStatement(inner.Item2) }.Concat(inner.Item1));
                    handler = new FinalizerRewriter(_stateVariableName, _labelStates).Process(handler);
                }
                else
                {
                    handler = stmt.Finally;
                }

                _finallyHandlers.Add(Tuple.Create(handlerName, handler));
                var stateAfter         = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState);
                var innerState         = CreateNewStateValue(currentState.FinallyStack, handlerName);
                var stateBeforeFinally = CreateNewStateValue(innerState.FinallyStack);
                currentBlock.Add(new JsSetNextStateStatement(innerState.StateValue));
                currentBlock.AddRange(Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.GuardedStatement, 0)), breakStack, continueStack, new State(currentState.LoopLabelName, currentState.StateValue, innerState.FinallyStack), stateBeforeFinally, false, false));

                Enqueue(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(JsStatement.Block(JsStatement.BlockMerged(new JsStatement[0])), 0)), breakStack, continueStack, stateBeforeFinally, stateAfter.Item1);
                if (!stack.IsEmpty || location.Index < location.Block.Statements.Count - 1)
                {
                    Enqueue(PushFollowing(stack, location), breakStack, continueStack, stateAfter.Item1, returnState);
                }
                return(false);
            }
            else if (_isIteratorBlock && stmt.Finally != null && !currentState.FinallyStack.IsEmpty)
            {
                // This is necessary to special-case in order to ensure that the inner finally block is executed before all outer ones.
                return(HandleTryStatement(JsStatement.Try(JsStatement.Try(stmt.GuardedStatement, stmt.Catch, null), null, stmt.Finally), location, stack, breakStack, continueStack, currentState, returnState, currentBlock, isFirstStatement));
            }
            else
            {
                var rewriter = new NestedStatementFixer(breakStack, continueStack, currentState, _exitState.Value, _makeSetResult);
                JsBlockStatement guarded;
                var guardedConstructs = FindInterestingConstructsVisitor.Analyze(stmt.GuardedStatement);
                if ((guardedConstructs & (InterestingConstruct.Label | InterestingConstruct.Await)) != InterestingConstruct.None)
                {
                    if (!isFirstStatement)
                    {
                        var sv = CreateNewStateValue(currentState.FinallyStack);
                        Enqueue(stack.Push(location), breakStack, continueStack, sv, returnState);
                        currentBlock.Add(new JsGotoStateStatement(sv, currentState));
                        return(false);
                    }

                    var inner = ProcessInner(stmt.GuardedStatement, breakStack, continueStack, currentState.FinallyStack, currentState.StateValue);
                    guarded = JsStatement.Block(inner.Item1);
                    currentBlock.Add(new JsSetNextStateStatement(inner.Item2));
                }
                else
                {
                    guarded = rewriter.Process(stmt.GuardedStatement);
                }

                JsCatchClause @catch;
                if (stmt.Catch != null)
                {
                    if (FindInterestingConstructsVisitor.Analyze(stmt.Catch.Body, InterestingConstruct.Label))
                    {
                        var inner = ProcessInner(stmt.Catch.Body, breakStack, continueStack, currentState.FinallyStack, null);
                        @catch = JsStatement.Catch(stmt.Catch.Identifier, JsStatement.Block(new[] { new JsSetNextStateStatement(inner.Item2) }.Concat(inner.Item1)));
                    }
                    else
                    {
                        var body = rewriter.Process(stmt.Catch.Body);
                        @catch = ReferenceEquals(body, stmt.Catch.Body) ? stmt.Catch : JsStatement.Catch(stmt.Catch.Identifier, body);
                    }
                }
                else
                {
                    @catch = null;
                }

                JsBlockStatement @finally;
                if (stmt.Finally != null)
                {
                    if (FindInterestingConstructsVisitor.Analyze(stmt.Finally, InterestingConstruct.Label))
                    {
                        var inner = ProcessInner(stmt.Finally, breakStack, continueStack, currentState.FinallyStack, null);
                        @finally = JsStatement.Block(new[] { new JsSetNextStateStatement(inner.Item2) }.Concat(inner.Item1));
                    }
                    else
                    {
                        @finally = rewriter.Process(stmt.Finally);
                    }

                    if ((guardedConstructs & InterestingConstruct.Await) != InterestingConstruct.None)
                    {
                        // Wrap the finally block inside an 'if (doFinallyBlocks) {}'
                        @finally = JsStatement.Block(JsStatement.If(JsExpression.Identifier(_doFinallyBlocksVariableName), @finally, null));
                    }
                }
                else
                {
                    @finally = null;
                }

                if (currentBlock.Count > 0 && _childStates.ContainsKey(currentState.StateValue))
                {
                    var newBlock = JsStatement.If(JsExpression.Same(JsExpression.Identifier(_stateVariableName), JsExpression.Number(currentState.StateValue)), JsStatement.Block(currentBlock), null);
                    currentBlock.Clear();
                    currentBlock.Add(newBlock);
                }

                currentBlock.Add(JsStatement.Try(guarded, @catch, @finally));
                return(true);
            }
        }
        private bool HandleAwaitStatement(JsAwaitStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock)
        {
            var  stateAfter       = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState).Item1;
            bool createDummyState = false;

            if (stateAfter.StateValue == returnState.StateValue)
            {
                stateAfter       = CreateNewStateValue(currentState.FinallyStack);
                createDummyState = true;                        // We must never return to our parent state after an await because
            }

            currentBlock.Add(new JsSetNextStateStatement(stateAfter.StateValue));
            currentBlock.Add(JsExpression.Invocation(JsExpression.Member(stmt.Awaiter, stmt.OnCompletedMethodName), JsExpression.Identifier(_stateMachineMethodName)));
            if (_needDoFinallyBlocksVariable)
            {
                currentBlock.Add(JsExpression.Assign(JsExpression.Identifier(_doFinallyBlocksVariableName), JsExpression.False));
            }
            currentBlock.Add(JsStatement.Return());

            if (!stack.IsEmpty || location.Index < location.Block.Statements.Count - 1)
            {
                Enqueue(PushFollowing(stack, location), breakStack, continueStack, stateAfter, returnState);
            }
            if (createDummyState)
            {
                Enqueue(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(JsStatement.Block(JsStatement.BlockMerged(new JsStatement[0])), 0)), breakStack, continueStack, stateAfter, returnState);
            }

            return(false);
        }
        public virtual JsStatement VisitBlockStatement(JsBlockStatement statement, TData data)
        {
            var after = VisitStatements(statement.Statements, data);

            return(ReferenceEquals(after, statement.Statements) ? statement : (statement.MergeWithParent ? JsStatement.BlockMerged(after) : JsStatement.Block(after)));
        }