Ejemplo n.º 1
0
        /// <summary>
        /// Transform multiple python except handlers for a try block into a single catch body.
        /// </summary>
        /// <param name="ag"></param>
        /// <param name="variable">The variable for the exception in the catch block.</param>
        /// <returns>Null if there are no except handlers. Else the statement to go inside the catch handler</returns>
        private MSAst.Expression TransformHandlers(AstGenerator ag, out MSAst.ParameterExpression variable) {
            if (_handlers == null || _handlers.Length == 0) {
                variable = null;
                return null;
            }
            bool emittingFinally = ag._isEmittingFinally;
            ag._isEmittingFinally = false;
            try {
                MSAst.ParameterExpression exception = ag.GetTemporary("exception", typeof(Exception));
                MSAst.ParameterExpression extracted = ag.GetTemporary("extracted", typeof(object));

                // The variable where the runtime will store the exception.
                variable = exception;

                var tests = new List<Microsoft.Scripting.Ast.IfStatementTest>(_handlers.Length);
                MSAst.ParameterExpression converted = null;
                MSAst.Expression catchAll = null;

                for (int index = 0; index < _handlers.Length; index++) {
                    TryStatementHandler tsh = _handlers[index];

                    if (tsh.Test != null) {
                        Microsoft.Scripting.Ast.IfStatementTest ist;

                        //  translating:
                        //      except Test ...
                        //
                        //  generate following AST for the Test (common part):
                        //      CheckException(exception, Test)
                        MSAst.Expression test =
                            Ast.Call(
                                AstGenerator.GetHelperMethod("CheckException"),
                                ag.LocalContext,
                                extracted,
                                ag.TransformAsObject(tsh.Test)
                            );

                        if (tsh.Target != null) {
                            //  translating:
                            //      except Test, Target:
                            //          <body>
                            //  into:
                            //      if ((converted = CheckException(exception, Test)) != null) {
                            //          Target = converted;
                            //          traceback-header
                            //          <body>
                            //      }

                            if (converted == null) {
                                converted = ag.GetTemporary("converted");
                            }

                            ist = AstUtils.IfCondition(
                                Ast.NotEqual(
                                    Ast.Assign(converted, test),
                                    AstUtils.Constant(null)
                                ),
                                Ast.Block(
                                    tsh.Target.TransformSet(ag, SourceSpan.None, converted, PythonOperationKind.None),
                                    ag.AddDebugInfo(
                                        GetTracebackHeader(
                                            ag,
                                            exception,
                                            ag.Transform(tsh.Body)
                                        ),
                                        new SourceSpan(tsh.Start, tsh.Header)
                                    ),
                                    AstUtils.Empty()
                                )
                            );
                        } else {
                            //  translating:
                            //      except Test:
                            //          <body>
                            //  into:
                            //      if (CheckException(exception, Test) != null) {
                            //          traceback-header
                            //          <body>
                            //      }
                            ist = AstUtils.IfCondition(
                                Ast.NotEqual(
                                    test,
                                    AstUtils.Constant(null)
                                ),
                                ag.AddDebugInfo(
                                    GetTracebackHeader(
                                        ag,
                                        exception,
                                        ag.Transform(tsh.Body)
                                    ),
                                    new SourceSpan(tsh.Start, tsh.Header)
                                )
                            );
                        }

                        // Add the test to the if statement test cascade
                        tests.Add(ist);
                    } else {
                        Debug.Assert(index == _handlers.Length - 1);
                        Debug.Assert(catchAll == null);

                        //  translating:
                        //      except:
                        //          <body>
                        //  into:
                        //  {
                        //          traceback-header
                        //          <body>
                        //  }

                        catchAll = ag.AddDebugInfo(
                            GetTracebackHeader(ag, exception, ag.Transform(tsh.Body)),
                            new SourceSpan(tsh.Start, tsh.Header)
                        );
                    }
                }

                MSAst.Expression body = null;

                if (tests.Count > 0) {
                    // rethrow the exception if we have no catch-all block
                    if (catchAll == null) {
                        catchAll = Ast.Block(
                            ag.GetSaveLineNumberExpression(true),
                            Ast.Throw(
                                Ast.Call(
                                    typeof(ExceptionHelpers).GetMethod("UpdateForRethrow"),
                                    exception
                                )
                            )
                        );
                    }

                    body = AstUtils.If(
                        tests.ToArray(),                        
                        catchAll
                    );
                } else {
                    Debug.Assert(catchAll != null);
                    body = catchAll;
                }

                if (converted != null) {
                    ag.FreeTemp(converted);
                }
                ag.FreeTemp(exception);
                ag.FreeTemp(extracted);

                // Codegen becomes:
                //     extracted = PythonOps.SetCurrentException(exception)
                //      < dynamic exception analysis >
                return Ast.Block(
                    Ast.Assign(
                        extracted,
                        Ast.Call(
                            AstGenerator.GetHelperMethod("SetCurrentException"),
                            ag.LocalContext,
                            exception
                        )
                    ),
                    body,
                    AstUtils.Empty()
                );
            } finally {
                ag._isEmittingFinally = emittingFinally;
            }
        }
Ejemplo n.º 2
0
 /// <summary>
 /// Surrounds the body of an except block w/ the appropriate code for maintaining the traceback.
 /// </summary>
 internal static MSAst.Expression GetTracebackHeader(AstGenerator ag, MSAst.ParameterExpression exception, MSAst.Expression body) {
     // we are about to enter a except block.  We need to emit the line number update so we track
     // the line that the exception was thrown from.  We then need to build exc_info() so that
     // it's available.  Finally we clear the list of dynamic stack frames because they've all
     // been associated with this exception.
     return Ast.Block(
         // pass false so if we take another exception we'll add it to the frame list
         ag.GetSaveLineNumberExpression(false),
         Ast.Call(
             AstGenerator.GetHelperMethod("BuildExceptionInfo"),
             ag.LocalContext,
             exception
         ),
         body,
         AstUtils.Empty()
     );
 }
Ejemplo n.º 3
0
        private MSAst.Expression AddFinally(AstGenerator/*!*/ ag, MSAst.Expression/*!*/ body, MSAst.ParameterExpression nestedException) {
            if (_finally != null) {
                bool isEmitting = ag._isEmittingFinally;
                ag._isEmittingFinally = true;
                int loopId = ++ag._loopOrFinallyId;
                ag.LoopOrFinallyIds.Add(loopId, true);
                try {
                    Debug.Assert(nestedException != null);

                    MSAst.ParameterExpression nestedFrames = ag.GetTemporary("$nestedFrames", typeof(List<DynamicStackFrame>));

                    bool inFinally = ag.InFinally;
                    ag.InFinally = true;
                    MSAst.Expression @finally = ag.Transform(_finally);
                    ag.InFinally = inFinally;
                    if (@finally == null) {
                        // error reported during compilation
                        return null;
                    }

                    // 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(
                            ag.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,
                            ag.GetSaveLineNumberExpression(false)
                        ),

                        // clear the frames incase thae finally throws, and allow line number
                        // updates to proceed
                        ag.UpdateLineUpdated(false),
                        Ast.Assign(
                            nestedFrames,
                            Ast.Call(AstGenerator.GetHelperMethod("GetAndClearDynamicStackFrames"))
                        ),

                        // run the finally code
                        @finally,

                        // if the finally exits normally restore any previous exception info
                        Ast.Call(
                            AstGenerator.GetHelperMethod("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,
                            ag.UpdateLineUpdated(true)
                        )
                    );
                    ag.FreeTemp(nestedFrames);
                    ag.FreeTemp(nestedException);
                } finally {
                    ag._isEmittingFinally = isEmitting;
                    ag.LoopOrFinallyIds.Remove(loopId);
                }
            }
            return body;
        }