/// <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( 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 AsyncExceptionHandlerRewriter(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 finally blocks: /// 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 (Exception 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( 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 AsyncExceptionHandlerRewriter(containingSymbol, containingType, factory, compilation, diagnostics, analysis); var loweredStatement = (BoundStatement)rewriter.Visit(statement); return loweredStatement; }