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