internal void GenerateMoveNextAndDispose(BoundStatement body, SynthesizedImplementationMethod moveNextMethod, SynthesizedImplementationMethod disposeMethod) { // scan body for yielding Trys _yieldsInTryAnalysis = new YieldsInTryAnalysis(body); if (_yieldsInTryAnalysis.ContainsYieldsInTrys()) { // adjust for the method Try/Fault block that we will put around the body. _tryNestingLevel++; } /////////////////////////////////// // Generate the body for MoveNext() /////////////////////////////////// F.CurrentMethod = moveNextMethod; int initialState; GeneratedLabelSymbol initialLabel; AddState(out initialState, out initialLabel); var newBody = (BoundStatement)Visit(body); // switch(cachedState) { // case 0: goto state_0; // case 1: goto state_1; // //etc // default: return false; // } // state_0: // state = -1; // [[rewritten body]] newBody = F.Block(ImmutableArray.Create(cachedState), F.Block( F.HiddenSequencePoint(), F.Assignment(F.Local(cachedState), F.Field(F.This(), stateField)) ), Dispatch(), GenerateReturn(finished: true), F.Label(initialLabel), F.Assignment(F.Field(F.This(), stateField), F.Literal(StateMachineStates.NotStartedStateMachine)), newBody); // // C# spec requires that iterators trap all exceptions and self-dispose eagerly. // // 10.14.4.1 The MoveNext method // . . . // When an exception is thrown and propagated out of the iterator block: // o Appropriate finally blocks in the iterator body will have been executed by the exception propagation. // o The state of the enumerator object is changed to after. // o The exception propagation continues to the caller of the MoveNext method. // . . . // if (_yieldsInTryAnalysis.ContainsYieldsInTrys()) { // try // { // body; // } // fault // { // this.Dispose(); // } var faultBlock = F.Block(F.ExpressionStatement(F.Call(F.This(), disposeMethod))); newBody = F.Fault((BoundBlock)newBody, faultBlock); } newBody = HandleReturn(newBody); F.CloseMethod(F.SequencePoint(body.Syntax, newBody)); /////////////////////////////////// // Generate the body for Dispose(). /////////////////////////////////// F.CurrentMethod = disposeMethod; var rootFrame = _currentFinallyFrame; if (rootFrame.knownStates == null) { // nothing to finalize F.CloseMethod(F.Return()); } else { var stateLocal = F.SynthesizedLocal(stateField.Type); var state = F.Local(stateLocal); var disposeBody = F.Block( ImmutableArray.Create<LocalSymbol>(stateLocal), F.Assignment(F.Local(stateLocal), F.Field(F.This(), stateField)), EmitFinallyFrame(rootFrame, state), F.Return()); F.CloseMethod(disposeBody); } }
internal void GenerateMoveNextAndDispose(BoundStatement body, SynthesizedImplementationMethod moveNextMethod, SynthesizedImplementationMethod disposeMethod) { // scan body for yielding try blocks _yieldsInTryAnalysis = new YieldsInTryAnalysis(body); if (_yieldsInTryAnalysis.ContainsYieldsInTrys()) { // adjust for the method Try/Fault block that we will put around the body. _tryNestingLevel++; } /////////////////////////////////// // Generate the body for MoveNext() /////////////////////////////////// F.CurrentFunction = moveNextMethod; int initialState; GeneratedLabelSymbol initialLabel; AddState(out initialState, out initialLabel); var newBody = (BoundStatement)Visit(body); // switch(cachedState) { // case 0: goto state_0; // case 1: goto state_1; // //etc // default: return false; // } // state_0: // state = -1; // [optional: cachedThis = capturedThis;] // [[rewritten body]] newBody = F.Block((object)cachedThis == null ? ImmutableArray.Create(cachedState) : ImmutableArray.Create(cachedState, cachedThis), F.HiddenSequencePoint(), F.Assignment(F.Local(cachedState), F.Field(F.This(), stateField)), CacheThisIfNeeded(), Dispatch(), GenerateReturn(finished: true), F.Label(initialLabel), F.Assignment(F.Field(F.This(), stateField), F.Literal(StateMachineStates.NotStartedStateMachine)), newBody); // // C# spec requires that iterators trap all exceptions and self-dispose eagerly. // // 10.14.4.1 The MoveNext method // . . . // When an exception is thrown and propagated out of the iterator block: // o Appropriate finally blocks in the iterator body will have been executed by the exception propagation. // o The state of the enumerator object is changed to after. // o The exception propagation continues to the caller of the MoveNext method. // . . . // if (_yieldsInTryAnalysis.ContainsYieldsInTrys()) { // try // { // body; // } // fault // { // this.Dispose(); // } var faultBlock = F.Block(F.ExpressionStatement(F.Call(F.This(), disposeMethod))); newBody = F.Fault((BoundBlock)newBody, faultBlock); } newBody = HandleReturn(newBody); F.CloseMethod(F.SequencePoint(body.Syntax, newBody)); /////////////////////////////////// // Generate the body for Dispose(). /////////////////////////////////// F.CurrentFunction = disposeMethod; var rootFrame = _currentFinallyFrame; if (rootFrame.knownStates == null) { // nothing to finalize F.CloseMethod(F.Return()); } else { var stateLocal = F.SynthesizedLocal(stateField.Type.TypeSymbol); var state = F.Local(stateLocal); var disposeBody = F.Block( ImmutableArray.Create <LocalSymbol>(stateLocal), F.Assignment(F.Local(stateLocal), F.Field(F.This(), stateField)), EmitFinallyFrame(rootFrame, state), F.Return()); F.CloseMethod(disposeBody); } }