/// <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 factory = new SyntheticBoundNodeFactory(containingSymbol, statement.Syntax, compilationState, diagnostics); var rewriter = new AsyncExceptionHandlerRewriter(containingSymbol, containingType, factory, 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 ensure 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 /// ensure: /// handler /// /// Into ===> /// /// ex as Exception = null /// pendingBranch as int = 0 /// /// try: /// code // any gotos/returns are rewritten to code that pends the necessary info and goes to finallyLabel /// goto finallyLabel /// except ex: // essentially pend the currently active exception /// pass /// /// :finallyLabel /// handler /// if ex != null: raise ex // unpend the exception /// unpend branches/return /// /// 2) Await containing catches: /// try: /// code /// except ex as Exception: /// handler /// raise /// /// /// Into ===> /// /// pendingException as Object /// pendingCatch as int = 0 /// /// try: /// code /// except temp as Exception: // essentially pend the currently active exception /// pendingException = temp /// pendingCatch = 1 /// /// if pendingCatch == 1: /// var ex = pendingException cast Exception /// handler /// raise pendingException /// </summary> public static void Rewrite(Method containingMethod) { if (containingMethod == null) { throw new ArgumentNullException("containingMethod"); } var body = containingMethod.Body; var analysis = new AwaitInFinallyAnalysis(body); if (analysis.ContainsAwaitInHandlers()) { var factory = CompilerContext.Current.CodeBuilder; var rewriter = new AsyncExceptionHandlerRewriter(containingMethod, factory, analysis); containingMethod.Body = (Block)rewriter.Visit(body); } }
/// <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; }