/// <summary>
        /// Lower a block of code by performing local rewritings.
        /// The goal is to not have exception handlers that contain awaits in them.
        ///
        /// 1) Await containing finallies:
        ///     The general strategy is to rewrite await containing handlers into synthetic handlers.
        ///     Synthetic handlers are not handlers in IL sense so it is ok to have awaits in them.
        ///     Since synthetic handlers are just blocks, we have to deal with pending exception/branch/return manually
        ///     (this is the hard part of the rewrite).
        ///
        ///     try{
        ///        code;
        ///     }finally{
        ///        handler;
        ///     }
        ///
        /// Into ===>
        ///
        ///     Exception ex = null;
        ///     int pendingBranch = 0;
        ///
        ///     try{
        ///         code;  // any gotos/returns are rewritten to code that pends the necessary info and goes to finallyLabel
        ///         goto finallyLabel;
        ///     }catch (ex){  // essentially pend the currently active exception
        ///     };
        ///
        ///     finallyLabel:
        ///     {
        ///        handler;
        ///        if (ex != null) throw ex;     // unpend the exception
        ///        unpend branches/return
        ///     }
        ///
        /// 2) Await containing catches:
        ///     try{
        ///         code;
        ///     }catch (Exeption ex){
        ///         handler;
        ///         throw;
        ///     }
        ///
        ///
        /// Into ===>
        ///
        ///     Object pendingException;
        ///     int pendingCatch = 0;
        ///
        ///     try{
        ///         code;
        ///     }catch (Exception temp){  // essentially pend the currently active exception
        ///         pendingException = temp;
        ///         pendingCatch = 1;
        ///     };
        ///
        ///     switch(pendingCatch):
        ///     {
        ///        case 1:
        ///         {
        ///             Exception ex = (Exception)pendingException;
        ///             handler;
        ///             throw pendingException
        ///         }
        ///     }
        /// </summary>
        public static BoundStatement Rewrite(
            bool generateDebugInfo,
            MethodSymbol containingSymbol,
            NamedTypeSymbol containingType,
            BoundStatement statement,
            TypeCompilationState compilationState,
            DiagnosticBag diagnostics)
        {
            Debug.Assert(statement != null);
            Debug.Assert(compilationState != null);

            var analysis = new AwaitInFinallyAnalysis(statement);

            if (!analysis.ContainsAwaitInHandlers())
            {
                return(statement);
            }

            var compilation      = containingType.DeclaringCompilation;
            var factory          = new SyntheticBoundNodeFactory(containingSymbol, statement.Syntax, compilationState, diagnostics);
            var rewriter         = new AsyncHandlerRewriter(generateDebugInfo, containingSymbol, containingType, factory, compilation, diagnostics, analysis);
            var loweredStatement = (BoundStatement)rewriter.Visit(statement);

            return(loweredStatement);
        }
        /// <summary>
        /// Lower a block of code by performing local rewritings. 
        /// The goal is to not have exception handlers that contain awaits in them.
        /// 
        /// 1) Await containing finallies:
        ///     The general strategy is to rewrite await containing handlers into synthetic handlers.
        ///     Synthetic handlers are not handlers in IL sense so it is ok to have awaits in them.
        ///     Since synthetic handlers are just blocks, we have to deal with pending exception/branch/return manually
        ///     (this is the hard part of the rewrite).
        ///
        ///     try{
        ///        code;
        ///     }finally{
        ///        handler;
        ///     }
        ///
        /// Into ===>
        ///
        ///     Exception ex = null;
        ///     int pendingBranch = 0;
        ///
        ///     try{
        ///         code;  // any gotos/returns are rewritten to code that pends the necessary info and goes to finallyLabel
        ///         goto finallyLabel;
        ///     }catch (ex){  // essentially pend the currently active exception
        ///     };
        ///
        ///     finallyLabel:
        ///     {
        ///        handler;
        ///        if (ex != null) throw ex;     // unpend the exception
        ///        unpend branches/return
        ///     }
        /// 
        /// 2) Await containing catches:
        ///     try{
        ///         code;
        ///     }catch (Exeption ex){
        ///         handler;
        ///         throw;
        ///     }
        /// 
        /// 
        /// Into ===>
        ///
        ///     Object pendingException;
        ///     int pendingCatch = 0;
        ///
        ///     try{
        ///         code; 
        ///     }catch (Exception temp){  // essentially pend the currently active exception
        ///         pendingException = temp;
        ///         pendingCatch = 1;
        ///     };
        ///
        ///     switch(pendingCatch):
        ///     {
        ///        case 1:
        ///         {
        ///             Exception ex = (Exception)pendingException;
        ///             handler;
        ///             throw pendingException
        ///         }
        ///     }
        /// </summary>
        public static BoundStatement Rewrite(
            bool generateDebugInfo,
            MethodSymbol containingSymbol,
            NamedTypeSymbol containingType,
            BoundStatement statement,
            TypeCompilationState compilationState,
            DiagnosticBag diagnostics)
        {
            Debug.Assert(containingSymbol != null);
            Debug.Assert(containingType != null);
            Debug.Assert(statement != null);
            Debug.Assert(compilationState != null);
            Debug.Assert(diagnostics != null);

            var analysis = new AwaitInFinallyAnalysis(statement);
            if (!analysis.ContainsAwaitInHandlers())
            {
                return statement;
            }

            var compilation = containingType.DeclaringCompilation;
            var factory = new SyntheticBoundNodeFactory(containingSymbol, statement.Syntax, compilationState, diagnostics);
            var rewriter = new AsyncHandlerRewriter(generateDebugInfo, containingSymbol, containingType, factory, compilation, diagnostics, analysis);
            var loweredStatement = (BoundStatement)rewriter.Visit(statement);

            return loweredStatement;
        }
Exemple #3
0
        internal static BoundStatement LowerStatement(
            bool generateDebugInfo,
            MethodSymbol method,
            BoundStatement body,
            SynthesizedSubmissionFields previousSubmissionFields,
            TypeCompilationState compilationState,
            DiagnosticBag diagnostics)
        {
            if (body.HasErrors)
            {
                return(body);
            }

            bool sawLambdas;
            bool sawDynamicOperations;
            bool sawAwaitInExceptionHandler;
            var  loweredBody = LocalRewriter.Rewrite(
                method.DeclaringCompilation,
                generateDebugInfo,
                method,
                method.ContainingType,
                body,
                compilationState,
                diagnostics,
                previousSubmissionFields,
                out sawLambdas,
                out sawDynamicOperations,
                out sawAwaitInExceptionHandler);

            if (sawDynamicOperations && compilationState.ModuleBuilder.IsENCDelta)
            {
                // Dynamic operations are not supported in ENC.
                var location = method.Locations[0];
                diagnostics.Add(new CSDiagnosticInfo(ErrorCode.ERR_EnCNoDynamicOperation), location);
            }

            if (loweredBody.HasErrors)
            {
                return(loweredBody);
            }

            if (sawAwaitInExceptionHandler)
            {
                // If we have awaits in handlers, we need to
                // replace handlers with synthetic ones which can be consumed by async rewriter.
                // The reason why this rewrite happens before the lambda rewrite
                // is that we may need access to exception locals and it would be fairly hard to do
                // if these locals are captured into closures (possibly nested ones).
                Debug.Assert(method.IteratorElementType == null);
                loweredBody = AsyncHandlerRewriter.Rewrite(
                    generateDebugInfo,
                    method,
                    method.ContainingType,
                    loweredBody,
                    compilationState,
                    diagnostics);
            }

            if (loweredBody.HasErrors)
            {
                return(loweredBody);
            }

            BoundStatement bodyWithoutLambdas = loweredBody;

            if (sawLambdas)
            {
                LambdaRewriter.Analysis lambdaAnalysis = LambdaRewriter.Analysis.Analyze(loweredBody, method, out sawLambdas);
                if (sawLambdas)
                {
                    bodyWithoutLambdas = LambdaRewriter.Rewrite(loweredBody, method.ContainingType, method.ThisParameter, method, compilationState, diagnostics, lambdaAnalysis, generateDebugInfo);
                }
            }

            if (bodyWithoutLambdas.HasErrors)
            {
                return(bodyWithoutLambdas);
            }

            BoundStatement bodyWithoutIterators = IteratorRewriter.Rewrite(bodyWithoutLambdas, method, compilationState, diagnostics, generateDebugInfo);

            if (bodyWithoutIterators.HasErrors)
            {
                return(bodyWithoutIterators);
            }

            BoundStatement bodyWithoutAsync = AsyncRewriter2.Rewrite(bodyWithoutIterators, method, compilationState, diagnostics, generateDebugInfo);

            return(bodyWithoutAsync);
        }