/// <summary>
            /// Generate the body for <c>MoveNext()</c>.
            /// </summary>
            internal void GenerateMoveNext(BoundStatement body, MethodSymbol moveNextMethod)
            {
                F.CurrentMethod = moveNextMethod;

                int initialState;
                GeneratedLabelSymbol initialLabel;

                AddState(out initialState, out initialLabel);

                var exceptionLocal = F.SynthesizedLocal(F.WellKnownType(WellKnownType.System_Exception), GeneratedNames.AsyncExceptionFieldName());

                var bodyBuilder = ArrayBuilder <BoundStatement> .GetInstance();

                bodyBuilder.Add(
                    F.HiddenSequencePoint());
                bodyBuilder.Add(
                    F.Assignment(F.Local(cachedState), F.Field(F.This(), stateField)));

                BoundStatement rewrittenBody = (BoundStatement)Visit(body);

                bodyBuilder.Add(
                    F.Try(
                        F.Block(
                            ImmutableArray <LocalSymbol> .Empty,
                            // switch (state) ...
                            F.HiddenSequencePoint(),
                            Dispatch(),
                            F.Label(initialLabel),
                            // [body]
                            rewrittenBody
                            ),
                        F.CatchBlocks(
                            F.Catch(
                                exceptionLocal,
                                F.Block(
                                    F.NoOp(method.ReturnsVoid ? NoOpStatementFlavor.AsyncMethodCatchHandler : NoOpStatementFlavor.Default),
                                    F.HiddenSequencePoint(),
                                    // this.state = finishedState
                                    F.Assignment(F.Field(F.This(), stateField), F.Literal(StateMachineStates.FinishedStateMachine)),
                                    // builder.SetException(ex)
                                    F.ExpressionStatement(
                                        F.Call(
                                            F.Field(F.This(), asyncMethodBuilderField),
                                            asyncMethodBuilderMemberCollection.SetException,
                                            F.Local(exceptionLocal))),
                                    GenerateReturn(false)
                                    )
                                )
                            )
                        ));

                // ReturnLabel (for the rewritten return expressions in the user's method body)
                bodyBuilder.Add(F.Label(exprReturnLabel));

                // this.state = finishedState
                var stateDone = F.Assignment(F.Field(F.This(), stateField), F.Literal(StateMachineStates.FinishedStateMachine));
                var block     = body.Syntax as BlockSyntax;

                if (block == null)
                {
                    // this happens, for example, in (async () => await e) where there is no block syntax
                    bodyBuilder.Add(stateDone);
                }
                else
                {
                    bodyBuilder.Add(F.SequencePointWithSpan(block, block.CloseBraceToken.Span, stateDone));
                    bodyBuilder.Add(F.HiddenSequencePoint());
                    // The remaining code is hidden to hide the fact that it can run concurrently with the task's continuation
                }

                // builder.SetResult([RetVal])
                bodyBuilder.Add(
                    F.ExpressionStatement(
                        F.Call(
                            F.Field(F.This(), asyncMethodBuilderField),
                            asyncMethodBuilderMemberCollection.SetResult,
                            method.IsGenericTaskReturningAsync(F.Compilation)
                                ? ImmutableArray.Create <BoundExpression>(F.Local(exprRetValue))
                                : ImmutableArray <BoundExpression> .Empty)));

                // this code is hidden behind a hidden sequence point.
                bodyBuilder.Add(F.Label(this.exitLabel));
                bodyBuilder.Add(F.Return());

                var newBody = bodyBuilder.ToImmutableAndFree();

                var locals = ArrayBuilder <LocalSymbol> .GetInstance();

                locals.Add(cachedState);
                if ((object)exprRetValue != null)
                {
                    locals.Add(exprRetValue);
                }

                F.CloseMethod(
                    F.SequencePoint(
                        body.Syntax,
                        F.Block(
                            locals.ToImmutableAndFree(),
                            newBody)));
            }
Exemplo n.º 2
0
            /// <summary>
            /// Generate the body for MoveNext()
            /// </summary>
            internal void GenerateMoveNext(BoundStatement body, MethodSymbol moveNextMethod)
            {
                F.CurrentMethod = moveNextMethod;

                int initialState;
                GeneratedLabelSymbol initialLabel;

                AddState(out initialState, out initialLabel);

                var exceptionLocal = F.SynthesizedLocal(F.WellKnownType(WellKnownType.System_Exception), GeneratedNames.AsyncExceptionFieldName());

                BoundStatement rewrittenBody = (BoundStatement)Visit(body);

                rewrittenBody = (BoundStatement) new AwaitLoweringRewriterPass1(F).Visit(rewrittenBody);
                rewrittenBody = (BoundStatement) new AwaitLoweringRewriterPass2(F, CompilationState).Visit(rewrittenBody);

                UnloweredSpillNodeVerifier.Verify(rewrittenBody);

                var bodyBuilder = ArrayBuilder <BoundStatement> .GetInstance();

                bodyBuilder.Add(
                    F.Try(
                        F.Block(
                            ReadOnlyArray <LocalSymbol> .Empty,
                            // switch (state) ...
                            Dispatch(),
                            // TODO(t-liam) - debugging support...
                            // dev11(LanguageAnalysis\Compiler\ResumableRewriter.cpp:325) does codegen here to add a
                            // debug nop and the 'NoCullNormalExit' label
                            F.Label(initialLabel),
                            // [body]
                            rewrittenBody
                            ),
                        F.CatchBlocks(
                            // TODO(t-liam) - debugging support...
                            // dev11(LanguageAnalysis\Compiler\ResumableRewriter.cpp:391)
                            // // Async void method generated catch handlers have their IL start offset
                            // // recorded to the PDB for special exception handling in the debugger.
                            // // Mark the EXPRHANDLER as such so we know to record its offset later.
                            F.Catch(
                                F.WellKnownType(WellKnownType.System_Exception),
                                exceptionLocal,
                                F.Block(
                                    // state = finishedState
                                    F.Assignment(F.Field(F.This(), state), F.Literal(StateMachineStates.FinishedStateMachine)),
                                    // builder.SetException(ex)
                                    F.ExpressionStatement(
                                        F.Call(
                                            F.Field(F.This(), asyncMethodBuilderField),
                                            asyncMethodBuilderMemberCollection.SetException,
                                            ReadOnlyArray <BoundExpression> .CreateFrom(F.Local(exceptionLocal)))),
                                    F.Return()
                                    )
                                )
                            )
                        ));

                // ReturnLabel (for the rewritten return expressions in the user's method body)
                bodyBuilder.Add(F.Label(exprReturnLabel));

                // state = finishedState
                bodyBuilder.Add(F.Assignment(F.Field(F.This(), state), F.Literal(StateMachineStates.FinishedStateMachine)));

                // builder.SetResult([RetVal])
                bodyBuilder.Add(
                    F.ExpressionStatement(
                        F.Call(
                            F.Field(F.This(), asyncMethodBuilderField),
                            asyncMethodBuilderMemberCollection.SetResult,
                            method.IsGenericTaskReturningAsync()
                                ? ReadOnlyArray <BoundExpression> .CreateFrom(F.Local(exprRetValue))
                                : ReadOnlyArray <BoundExpression> .Empty)));

                bodyBuilder.Add(F.Return());

                var newBody = bodyBuilder.ToReadOnlyAndFree();

                F.CloseMethod(
                    F.SequencePoint(
                        body.Syntax,
                        F.Block(
                            exprRetValue != null ? ReadOnlyArray <LocalSymbol> .CreateFrom(exprRetValue) : ReadOnlyArray <LocalSymbol> .Empty,
                            newBody)));
            }