/// <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); }