/// <summary> /// Generate the GetEnumerator() method for iterators and GetAsyncEnumerator() for async-iterators. /// </summary> protected SynthesizedImplementationMethod GenerateIteratorGetEnumerator(MethodSymbol getEnumeratorMethod, ref BoundExpression managedThreadId, int initialState) { // Produces: // {StateMachineType} result; // if (this.initialThreadId == {managedThreadId} && this.state == -2) // { // this.state = {initialState}; // extraReset // result = this; // } // else // { // result = new {StateMachineType}({initialState}); // } // result.parameter = this.parameterProxy; // copy all of the parameter proxies // The implementation doesn't depend on the method body of the iterator method. // Only on its parameters and staticness. var getEnumerator = OpenMethodImplementation( getEnumeratorMethod, hasMethodBodyDependency: false); var bodyBuilder = ArrayBuilder <BoundStatement> .GetInstance(); // {StateMachineType} result; var resultVariable = F.SynthesizedLocal(stateMachineType, null); // result = new {StateMachineType}({initialState}) BoundStatement makeIterator = F.Assignment(F.Local(resultVariable), F.New(stateMachineType.Constructor, F.Literal(initialState))); var thisInitialized = F.GenerateLabel("thisInitialized"); if ((object)initialThreadIdField != null) { managedThreadId = MakeCurrentThreadId(); var thenBuilder = ArrayBuilder <BoundStatement> .GetInstance(4); thenBuilder.Add( // this.state = {initialState}; F.Assignment(F.Field(F.This(), stateField), F.Literal(initialState))); thenBuilder.Add( // result = this; F.Assignment(F.Local(resultVariable), F.This())); var extraReset = GetExtraResetForIteratorGetEnumerator(); if (extraReset != null) { thenBuilder.Add(extraReset); } thenBuilder.Add( method.IsStatic || method.ThisParameter.Type.IsReferenceType ? // if this is a reference type, no need to copy it since it is not assignable F.Goto(thisInitialized) : // goto thisInitialized (BoundStatement)F.StatementList()); makeIterator = F.If( // if (this.state == -2 && this.initialThreadId == Thread.CurrentThread.ManagedThreadId) condition: F.LogicalAnd( F.IntEqual(F.Field(F.This(), stateField), F.Literal(StateMachineStates.FinishedStateMachine)), F.IntEqual(F.Field(F.This(), initialThreadIdField), managedThreadId)), thenClause: F.Block(thenBuilder.ToImmutableAndFree()), elseClauseOpt: makeIterator); } bodyBuilder.Add(makeIterator); // Initialize all the parameter copies var copySrc = initialParameters; var copyDest = nonReusableLocalProxies; if (!method.IsStatic) { // starting with "this" CapturedSymbolReplacement proxy; if (copyDest.TryGetValue(method.ThisParameter, out proxy)) { bodyBuilder.Add( F.Assignment( proxy.Replacement(F.Syntax, stateMachineType => F.Local(resultVariable)), copySrc[method.ThisParameter].Replacement(F.Syntax, stateMachineType => F.This()))); } } bodyBuilder.Add(F.Label(thisInitialized)); foreach (var parameter in method.Parameters) { CapturedSymbolReplacement proxy; if (copyDest.TryGetValue(parameter, out proxy)) { bodyBuilder.Add( F.Assignment( proxy.Replacement(F.Syntax, stateMachineType => F.Local(resultVariable)), copySrc[parameter].Replacement(F.Syntax, stateMachineType => F.This()))); } } bodyBuilder.Add(F.Return(F.Local(resultVariable))); F.CloseMethod(F.Block(ImmutableArray.Create(resultVariable), bodyBuilder.ToImmutableAndFree())); return(getEnumerator); }
/// <summary> /// Generate the GetEnumerator() method for iterators and GetAsyncEnumerator() for async-iterators. /// </summary> protected SynthesizedImplementationMethod GenerateIteratorGetEnumerator(MethodSymbol getEnumeratorMethod, ref BoundExpression managedThreadId, int initialState) { // Produces: // {StateMachineType} result; // if (this.initialThreadId == {managedThreadId} && this.state == -2) // { // this.state = {initialState}; // extraReset // result = this; // } // else // { // result = new {StateMachineType}({initialState}); // } // // // Initialize each parameter fields // result.parameter = this.parameterProxy; // OR // if (token.Equals(default)) { result.parameter = this.parameterProxy; } else { result.parameter = token; } // for async-enumerable parameters marked with [EnumeratorCancellation] // The implementation doesn't depend on the method body of the iterator method. // Only on its parameters and staticness. var getEnumerator = OpenMethodImplementation( getEnumeratorMethod, hasMethodBodyDependency: false); var bodyBuilder = ArrayBuilder <BoundStatement> .GetInstance(); // {StateMachineType} result; var resultVariable = F.SynthesizedLocal(stateMachineType, null); // result = new {StateMachineType}({initialState}) BoundStatement makeIterator = F.Assignment(F.Local(resultVariable), F.New(stateMachineType.Constructor, F.Literal(initialState))); var thisInitialized = F.GenerateLabel("thisInitialized"); if ((object)initialThreadIdField != null) { managedThreadId = MakeCurrentThreadId(); var thenBuilder = ArrayBuilder <BoundStatement> .GetInstance(4); thenBuilder.Add( // this.state = {initialState}; F.Assignment(F.Field(F.This(), stateField), F.Literal(initialState))); thenBuilder.Add( // result = this; F.Assignment(F.Local(resultVariable), F.This())); var extraReset = GetExtraResetForIteratorGetEnumerator(); if (extraReset != null) { thenBuilder.Add(extraReset); } if (method.IsStatic || method.ThisParameter.Type.IsReferenceType) { // if this is a reference type, no need to copy it since it is not assignable thenBuilder.Add( // goto thisInitialized; F.Goto(thisInitialized)); } makeIterator = F.If( // if (this.state == -2 && this.initialThreadId == Thread.CurrentThread.ManagedThreadId) condition: F.LogicalAnd( F.IntEqual(F.Field(F.This(), stateField), F.Literal(StateMachineStates.FinishedStateMachine)), F.IntEqual(F.Field(F.This(), initialThreadIdField), managedThreadId)), thenClause: F.Block(thenBuilder.ToImmutableAndFree()), elseClauseOpt: makeIterator); } bodyBuilder.Add(makeIterator); // Initialize all the parameter copies var copySrc = initialParameters; var copyDest = nonReusableLocalProxies; if (!method.IsStatic) { // starting with "this" CapturedSymbolReplacement proxy; if (copyDest.TryGetValue(method.ThisParameter, out proxy)) { bodyBuilder.Add( F.Assignment( proxy.Replacement(F.Syntax, stateMachineType => F.Local(resultVariable)), copySrc[method.ThisParameter].Replacement(F.Syntax, stateMachineType => F.This()))); } } bodyBuilder.Add(F.Label(thisInitialized)); foreach (var parameter in method.Parameters) { CapturedSymbolReplacement proxy; if (copyDest.TryGetValue(parameter, out proxy)) { // result.parameter = this.parameterProxy; BoundExpression left = proxy.Replacement(F.Syntax, stateMachineType => F.Local(resultVariable)); BoundStatement copy = F.Assignment( left, copySrc[parameter].Replacement(F.Syntax, stateMachineType => F.This())); if (this.method.IsAsync) { ParameterSymbol tokenParameter = getEnumeratorMethod.Parameters[0]; if (hasEnumeratorCancellationAttribute(parameter)) { // For any async-enumerable parameter marked with [EnumeratorCancellation] attribute, conditionally copy GetAsyncEnumerator's cancellation token parameter instead // if (token.Equals(default)) // result.parameter = this.parameterProxy; // else // result.parameter = token; copy = F.If( // if (token.Equals(default)) F.Call(F.Parameter(tokenParameter), WellKnownMember.System_Threading_CancellationToken__Equals, F.Default(tokenParameter.Type)), // result.parameter = this.parameterProxy; copy, // result.parameter = token; F.Assignment(left, F.Parameter(tokenParameter))); } } bodyBuilder.Add(copy); } } bodyBuilder.Add(F.Return(F.Local(resultVariable))); F.CloseMethod(F.Block(ImmutableArray.Create(resultVariable), bodyBuilder.ToImmutableAndFree())); return(getEnumerator);