Example #1
0
        /// <summary>
        /// The try statement is the most complex part of the state machine transformation.
        /// Since the CLR will not allow a 'goto' into the scope of a try statement, we must
        /// generate the dispatch to the state's label stepwise.  That is done by translating
        /// the try statements from the inside to the outside.  Within a try statement, we
        /// start with an empty dispatch table (representing the mapping from state numbers
        /// to labels).  During translation of the try statement's body, the dispatch table
        /// will be filled in with the data necessary to dispatch once we're inside the try
        /// block.  We generate that at the head of the translated try statement.  Then, we
        /// copy all of the states from that table into the table for the enclosing construct,
        /// but associate them with a label just before the translated try block.  That way
        /// the enclosing construct will generate the code necessary to get control into the
        /// try block for all of those states.
        /// </summary>
        public override BoundNode VisitTryStatement(BoundTryStatement node)
        {
            var oldDispatches        = _dispatches;
            var oldFinalizerState    = _currentFinalizerState;
            var oldHasFinalizerState = _hasFinalizerState;

            _dispatches            = null;
            _currentFinalizerState = -1;
            _hasFinalizerState     = false;

            BoundBlock           tryBlock      = F.Block((BoundStatement)this.Visit(node.TryBlock));
            GeneratedLabelSymbol dispatchLabel = null;

            if (_dispatches != null)
            {
                dispatchLabel = F.GenerateLabel("tryDispatch");
                if (_hasFinalizerState)
                {
                    // cause the current finalizer state to arrive here and then "return false"
                    var finalizer = F.GenerateLabel("finalizer");
                    _dispatches.Add(finalizer, new List <int>()
                    {
                        _currentFinalizerState
                    });
                    var skipFinalizer = F.GenerateLabel("skipFinalizer");
                    tryBlock = F.Block(
                        F.HiddenSequencePoint(),
                        Dispatch(),
                        F.Goto(skipFinalizer),
                        F.Label(finalizer), // code for the finalizer here
                        GenerateSetBothStates(StateMachineStates.NotStartedStateMachine),
                        GenerateReturn(false),
                        F.Label(skipFinalizer),
                        tryBlock);
                }
                else
                {
                    tryBlock = F.Block(
                        F.HiddenSequencePoint(),
                        Dispatch(),
                        tryBlock);
                }

                if (oldDispatches == null)
                {
                    Debug.Assert(!oldHasFinalizerState);
                    oldDispatches = new Dictionary <LabelSymbol, List <int> >();
                }

                oldDispatches.Add(dispatchLabel, new List <int>(from kv in _dispatches.Values from n in kv orderby n select n));
            }

            _hasFinalizerState     = oldHasFinalizerState;
            _currentFinalizerState = oldFinalizerState;
            _dispatches            = oldDispatches;

            ImmutableArray <BoundCatchBlock> catchBlocks = this.VisitList(node.CatchBlocks);
            BoundBlock finallyBlockOpt = node.FinallyBlockOpt == null ? null : F.Block(
                F.HiddenSequencePoint(),
                F.If(
                    condition: ShouldEnterFinallyBlock(),
                    thenClause: (BoundBlock)this.Visit(node.FinallyBlockOpt)
                    ),
                F.HiddenSequencePoint());

            BoundStatement result = node.Update(tryBlock, catchBlocks, finallyBlockOpt, node.FinallyLabelOpt, node.PreferFaultHandler);

            if ((object)dispatchLabel != null)
            {
                result = F.Block(
                    F.HiddenSequencePoint(),
                    F.Label(dispatchLabel),
                    result);
            }

            return(result);
        }