internal override MSAst.Expression Transform(AstGenerator ag) { // allocated all variables here so they won't be shared w/ other // locals allocated during the body or except blocks. MSAst.ParameterExpression noNestedException = null; if (_finally != null) { noNestedException = ag.GetTemporary("$noException", typeof(bool)); } MSAst.ParameterExpression lineUpdated = null; MSAst.ParameterExpression runElse = null; if (_else != null || (_handlers != null && _handlers.Length > 0)) { lineUpdated = ag.GetTemporary("$lineUpdated", typeof(bool)); if (_else != null) { runElse = ag.GetTemporary("run_else", typeof(bool)); } } // don't allocate locals below here... MSAst.Expression body = ag.Transform(_body); MSAst.Expression @else = ag.Transform(_else); if (body == null) { return null; } MSAst.ParameterExpression exception; MSAst.Expression @catch = TransformHandlers(ag, out exception); MSAst.Expression result; // 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, Ast.Constant(true)), // save existing line updated, we could choose to do this only for nested exception handlers. ag.PushLineUpdated(false, lineUpdated), AstUtils.Try( ag.AddDebugInfo(Ast.Empty(), new SourceSpan(Span.Start, _header)), body ).Catch(exception, Ast.Assign(runElse, Ast.Constant(false)), @catch, // restore existing line updated after exception handler completes ag.PopLineUpdated(lineUpdated), Ast.Default(body.Type) ), AstUtils.IfThen(runElse, @else ), Ast.Empty() ); } else if (@catch != null) { // no "else" clause // try { // <try body> // } catch (Exception e) { // ... catch handling ... // } // result = AstUtils.Try( ag.AddDebugInfo(Ast.Empty(), new SourceSpan(Span.Start, _header)), // save existing line updated ag.PushLineUpdated(false, lineUpdated), body ).Catch(exception, @catch, // restore existing line updated after exception handler completes ag.PopLineUpdated(lineUpdated), Ast.Default(body.Type) ); } else { result = body; } try { return AddFinally(ag, result, noNestedException); } finally { // free all locals here after the children nodes have been generated if (lineUpdated != null) { ag.FreeTemp(lineUpdated); } if (runElse != null) { ag.FreeTemp(@runElse); } } }
/// <summary> /// WithStatement is translated to the DLR AST equivalent to /// the following Python code snippet (from with statement spec): /// /// mgr = (EXPR) /// exit = mgr.__exit__ # Not calling it yet /// value = mgr.__enter__() /// exc = True /// try: /// VAR = value # Only if "as VAR" is present /// BLOCK /// except: /// # The exceptional case is handled here /// exc = False /// if not exit(*sys.exc_info()): /// raise /// # The exception is swallowed if exit() returns true /// finally: /// # The normal and non-local-goto cases are handled here /// if exc: /// exit(None, None, None) /// /// </summary> internal override MSAst.Expression Transform(AstGenerator ag) { MSAst.ParameterExpression lineUpdated = ag.GetTemporary("$lineUpdated_with", typeof(bool)); // Five statements in the result... MSAst.Expression[] statements = new MSAst.Expression[6]; //****************************************************************** // 1. mgr = (EXPR) //****************************************************************** MSAst.ParameterExpression manager = ag.GetTemporary("with_manager"); statements[0] = ag.MakeAssignment( manager, ag.Transform(_contextManager), new SourceSpan(Start, _header) ); //****************************************************************** // 2. exit = mgr.__exit__ # Not calling it yet //****************************************************************** MSAst.ParameterExpression exit = ag.GetTemporary("with_exit"); statements[1] = ag.MakeAssignment( exit, ag.Get( typeof(object), "__exit__", manager ) ); //****************************************************************** // 3. value = mgr.__enter__() //****************************************************************** MSAst.ParameterExpression value = ag.GetTemporary("with_value"); statements[2] = ag.AddDebugInfo( ag.MakeAssignment( value, ag.Invoke( typeof(object), new CallSignature(0), ag.Get( typeof(object), "__enter__", manager ) ) ), new SourceSpan(Start, _header) ); //****************************************************************** // 4. exc = True //****************************************************************** MSAst.ParameterExpression exc = ag.GetTemporary("with_exc", typeof(bool)); statements[3] = ag.MakeAssignment( exc, AstUtils.Constant(true) ); //****************************************************************** // 5. The final try statement: // // try: // VAR = value # Only if "as VAR" is present // BLOCK // except: // # The exceptional case is handled here // exc = False // if not exit(*sys.exc_info()): // raise // # The exception is swallowed if exit() returns true // finally: // # The normal and non-local-goto cases are handled here // if exc: // exit(None, None, None) //****************************************************************** MSAst.ParameterExpression exception = ag.GetTemporary("exception", typeof(Exception)); MSAst.ParameterExpression nestedFrames = ag.GetTemporary("$nestedFrames", typeof(List<DynamicStackFrame>)); statements[4] = // try: AstUtils.Try( AstUtils.Try(// try statement body ag.PushLineUpdated(false, lineUpdated), _var != null ? ag.AddDebugInfo( Ast.Block( // VAR = value _var.TransformSet(ag, SourceSpan.None, value, PythonOperationKind.None), // BLOCK ag.Transform(_body), AstUtils.Empty() ), _body.Span ) : // BLOCK ag.Transform(_body) // except:, // try statement location ).Catch(exception, // Python specific exception handling code TryStatement.GetTracebackHeader( ag, exception, ag.AddDebugInfo( Ast.Block( // exc = False ag.MakeAssignment( exc, AstUtils.Constant(false) ), Ast.Assign( nestedFrames, Ast.Call(AstGenerator.GetHelperMethod("GetAndClearDynamicStackFrames")) ), // if not exit(*sys.exc_info()): // raise AstUtils.IfThen( ag.Convert( typeof(bool), ConversionResultKind.ExplicitCast, ag.Operation( typeof(bool), PythonOperationKind.Not, MakeExitCall(ag, exit, exception) ) ), ag.UpdateLineUpdated(true), Ast.Call( AstGenerator.GetHelperMethod("SetDynamicStackFrames"), nestedFrames ), Ast.Rethrow() ) ), _body.Span ) ), Ast.Call( AstGenerator.GetHelperMethod("SetDynamicStackFrames"), nestedFrames ), ag.PopLineUpdated(lineUpdated), Ast.Empty() ) // finally: ).Finally( // if exc: // exit(None, None, None) AstUtils.IfThen( exc, ag.AddDebugInfo( Ast.Block( Ast.Dynamic( ag.BinderState.Invoke( new CallSignature(3) // signature doesn't include function ), typeof(object), new MSAst.Expression[] { ag.LocalContext, exit, AstUtils.Constant(null), AstUtils.Constant(null), AstUtils.Constant(null) } ), Ast.Empty() ), _contextManager.Span ) ) ); statements[4] = ag.AddDebugInfo(statements[4], Span); statements[5] = AstUtils.Empty(); return ag.AddDebugInfo(Ast.Block(statements), _body.Span); }