private MSAst.Expression AddFinally(MSAst.Expression /*!*/ body) { if (_finally != null) { MSAst.ParameterExpression tryThrows = Ast.Variable(typeof(Exception), "$tryThrows"); MSAst.ParameterExpression locException = Ast.Variable(typeof(Exception), "$localException"); MSAst.Expression @finally = _finally; // lots is going on here. We need to consider: // 1. Exceptions propagating out of try/except/finally. Here we need to save the line # // from the exception block and not save the # from the finally block later. // 2. Exceptions propagating out of the finally block. Here we need to report the line number // from the finally block and leave the existing stack traces cleared. // 3. Returning from the try block: Here we need to run the finally block and not update the // line numbers. body = AstUtils.Try( // we use a fault to know when we have an exception and when control leaves normally (via // either a return or the body completing successfully). AstUtils.Try( Parent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, GlobalParent.IndexToLocation(_headerIndex))), Ast.Assign(tryThrows, AstUtils.Constant(null, typeof(Exception))), body, AstUtils.Empty() ).Catch( locException, Expression.Block( Ast.Assign(tryThrows, locException), Expression.Rethrow() ) ) ).FinallyWithJumps( // if we had an exception save the line # that was last executing during the try AstUtils.If( Expression.NotEqual(tryThrows, Expression.Default(typeof(Exception))), Parent.GetSaveLineNumberExpression(tryThrows, false) ), // clear the frames incase thae finally throws, and allow line number // updates to proceed UpdateLineUpdated(false), // run the finally code @finally, // if we took an exception in the try block we have saved the line number. Otherwise // we have no line number saved and will need to continue saving them if // other exceptions are thrown. AstUtils.If( Expression.NotEqual(tryThrows, Expression.Default(typeof(Exception))), UpdateLineUpdated(true) ) ); body = Ast.Block(new[] { tryThrows }, body); } return(body); }
public override MSAst.Expression Reduce() { // allocated all variables here so they won't be shared w/ other // locals allocated during the body or except blocks. MSAst.ParameterExpression lineUpdated = null; MSAst.ParameterExpression runElse = null; if (_else != null || (_handlers != null && _handlers.Length > 0)) { lineUpdated = Ast.Variable(typeof(bool), "$lineUpdated_try"); if (_else != null) { runElse = Ast.Variable(typeof(bool), "run_else"); } } // don't allocate locals below here... MSAst.Expression body = _body; MSAst.Expression @else = _else; MSAst.Expression @catch, result; MSAst.ParameterExpression exception; if (_handlers != null && _handlers.Length > 0) { exception = Ast.Variable(typeof(Exception), "$exception"); @catch = TransformHandlers(exception); } else { exception = null; @catch = null; } // We have else clause, must generate guard around it if (@else != null) { Debug.Assert(@catch != null); // run_else = true; // try { // try_body // } catch ( ... ) { // run_else = false; // catch_body // } // if (run_else) { // else_body // } result = Ast.Block( Ast.Assign(runElse, AstUtils.Constant(true)), // save existing line updated, we could choose to do this only for nested exception handlers. PushLineUpdated(false, lineUpdated), AstUtils.Try( Parent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, _header)), body ).Catch(exception, Ast.Assign(runElse, AstUtils.Constant(false)), @catch, // restore existing line updated after exception handler completes PopLineUpdated(lineUpdated), Ast.Assign(exception, Ast.Constant(null, typeof(Exception))), AstUtils.Default(body.Type) ), AstUtils.IfThen(runElse, @else ), AstUtils.Empty() ); } else if (@catch != null) // no "else" clause // try { // <try body> // } catch (Exception e) { // ... catch handling ... // } // { result = AstUtils.Try( GlobalParent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, _header)), // save existing line updated PushLineUpdated(false, lineUpdated), body ).Catch(exception, @catch, // restore existing line updated after exception handler completes PopLineUpdated(lineUpdated), Ast.Call(AstMethods.ExceptionHandled, Parent.LocalContext), Ast.Assign(exception, Ast.Constant(null, typeof(Exception))), AstUtils.Default(body.Type) ); } else { result = body; } return(Ast.Block( GetVariables(lineUpdated, runElse), AddFinally(result) )); }
private MSAst.Expression AddFinally(MSAst.Expression /*!*/ body, MSAst.ParameterExpression nestedException) { if (_finally != null) { Debug.Assert(nestedException != null); MSAst.ParameterExpression nestedFrames = Ast.Variable(typeof(List <DynamicStackFrame>), "$nestedFrames"); MSAst.Expression @finally = _finally; // lots is going on here. We need to consider: // 1. Exceptions propagating out of try/except/finally. Here we need to save the line # // from the exception block and not save the # from the finally block later. // 2. Exceptions propagating out of the finally block. Here we need to report the line number // from the finally block and leave the existing stack traces cleared. // 3. Returning from the try block: Here we need to run the finally block and not update the // line numbers. body = AstUtils.Try( // we use a fault to know when we have an exception and when control leaves normally (via // either a return or the body completing successfully). AstUtils.Try( Parent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, _header)), Ast.Assign(nestedException, AstUtils.Constant(false)), body ).Fault( // fault Ast.Assign(nestedException, AstUtils.Constant(true)) ) ).FinallyWithJumps( // if we had an exception save the line # that was last executing during the try AstUtils.If( nestedException, Parent.GetSaveLineNumberExpression(false) ), // clear the frames incase thae finally throws, and allow line number // updates to proceed UpdateLineUpdated(false), Ast.Assign( nestedFrames, Ast.Call(AstMethods.GetAndClearDynamicStackFrames) ), // run the finally code @finally, // if the finally exits normally restore any previous exception info Ast.Call( AstMethods.SetDynamicStackFrames, nestedFrames ), // if we took an exception in the try block we have saved the line number. Otherwise // we have no line number saved and will need to continue saving them if // other exceptions are thrown. AstUtils.If( nestedException, UpdateLineUpdated(true) ) ); body = Ast.Block(new[] { nestedFrames }, body); } return(body); }
public override MSAst.Expression Reduce() { // allocated all variables here so they won't be shared w/ other // locals allocated during the body or except blocks. MSAst.ParameterExpression?lineUpdated = null; MSAst.ParameterExpression?runElse = null; MSAst.ParameterExpression?previousExceptionContext = null; Debug.Assert(Else is null || _handlers.Length > 0); if (_handlers.Length > 0) { lineUpdated = Ast.Variable(typeof(bool), "$lineUpdated_try"); if (Else != null) { runElse = Ast.Variable(typeof(bool), "run_else"); } } // don't allocate locals below here... MSAst.Expression body = Body; MSAst.Expression? @else = Else; MSAst.Expression? @catch; MSAst.Expression result; MSAst.ParameterExpression?exception; if (_handlers.Length > 0) { previousExceptionContext = Ast.Variable(typeof(Exception), "$previousException"); exception = Ast.Variable(typeof(Exception), "$exception"); @catch = TransformHandlers(exception, previousExceptionContext); } else if (Finally != null) { exception = Ast.Variable(typeof(Exception), "$exception"); @catch = null; } else { exception = null; @catch = null; } // We have else clause, must generate guard around it if (@else != null) { Debug.Assert(runElse != null); Debug.Assert(previousExceptionContext != null && exception != null && @catch != null); // run_else = true; // try { // try_body // } catch ( ... ) { // run_else = false; // catch_body // } // if (run_else) { // else_body // } result = Ast.Block( Ast.Assign(runElse, AstUtils.Constant(true)), // save existing line updated, we could choose to do this only for nested exception handlers. PushLineUpdated(false, lineUpdated), LightExceptions.RewriteExternal( AstUtils.Try( Parent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, GlobalParent.IndexToLocation(HeaderIndex))), Ast.Assign(previousExceptionContext, Ast.Call(AstMethods.SaveCurrentException)), body, AstUtils.Constant(null) ).Catch(exception, Ast.Assign(runElse, AstUtils.Constant(false)), @catch, // restore existing line updated after exception handler completes PopLineUpdated(lineUpdated), Ast.Assign(exception, Ast.Constant(null, typeof(Exception))), AstUtils.Constant(null) ) ), AstUtils.IfThen(runElse, @else ), AstUtils.Empty() ); } else if (@catch != null) // no "else" clause // try { // <try body> // } catch (Exception e) { // ... catch handling ... // } // { Debug.Assert(previousExceptionContext != null && exception != null); result = LightExceptions.RewriteExternal( AstUtils.Try( GlobalParent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, GlobalParent.IndexToLocation(HeaderIndex))), // save existing line updated PushLineUpdated(false, lineUpdated), Ast.Assign(previousExceptionContext, Ast.Call(AstMethods.SaveCurrentException)), body, AstUtils.Constant(null) ).Catch(exception, @catch, // restore existing line updated after exception handler completes PopLineUpdated(lineUpdated), Ast.Assign(exception, Ast.Constant(null, typeof(Exception))), AstUtils.Constant(null) ) ); } else { result = body; } return(Ast.Block( GetVariables(lineUpdated, runElse, previousExceptionContext), AddFinally(result), AstUtils.Default(typeof(void)) )); }