IronRuby.Compiler.Ast.Body.TransformExceptionHandling C# (CSharp) Method

TransformExceptionHandling() private method

private TransformExceptionHandling ( AstGenerator gen, ResultOperation resultOperation ) : Expression
gen AstGenerator
resultOperation ResultOperation
return System.Linq.Expressions.Expression
        private MSA.Expression/*!*/ TransformExceptionHandling(AstGenerator/*!*/ gen, ResultOperation resultOperation) {
            Assert.NotNull(gen);

            MSA.Expression exceptionThrownVariable = gen.CurrentScope.DefineHiddenVariable("#exception-thrown", typeof(bool));
            MSA.Expression exceptionRethrowVariable = gen.CurrentScope.DefineHiddenVariable("#exception-rethrow", typeof(bool));
            MSA.Expression retryingVariable = gen.CurrentScope.DefineHiddenVariable("#retrying", typeof(bool));
            MSA.Expression oldExceptionVariable = gen.CurrentScope.DefineHiddenVariable("#old-exception", typeof(Exception));

            MSA.ParameterExpression unwinder, exceptionVariable;
            MSA.Expression transformedBody;
            MSA.Expression transformedEnsure;
            MSA.Expression transformedElse;

            if (_ensureStatements != null) {
                transformedEnsure = Ast.Block(
                    // ensure:
                    Ast.Assign(oldExceptionVariable, Methods.GetCurrentException.OpCall(gen.CurrentScopeVariable)),
                    gen.TransformStatements(_ensureStatements, ResultOperation.Ignore),
                    Methods.SetCurrentException.OpCall(gen.CurrentScopeVariable, oldExceptionVariable),

                    // rethrow:
                    AstUtils.IfThen(
                        Ast.AndAlso(
                            exceptionRethrowVariable,
                            Ast.NotEqual(oldExceptionVariable, AstUtils.Constant(null))
                        ),
                        Ast.Throw(oldExceptionVariable)
                    )
                );
            } else {
                // rethrow:
                transformedEnsure = AstUtils.IfThen(
                    Ast.AndAlso(
                        exceptionRethrowVariable,
                        Ast.NotEqual(
                            Ast.Assign(oldExceptionVariable, Methods.GetCurrentException.OpCall(gen.CurrentScopeVariable)),
                            AstUtils.Constant(null, typeof(Exception)))
                        ),
                    Ast.Throw(oldExceptionVariable)
                );
            }

            if (_elseStatements != null) {
                transformedElse = gen.TransformStatements(_elseStatements, resultOperation);
            } else {
                transformedElse = AstUtils.Empty();
            }

            // body should do return, but else-clause is present => we cannot do return from the guarded statements: 
            // (the value of the last expression in the body cannot be the last executed expression statement => we can ignore it):
            transformedBody = gen.TransformStatements(_statements, (_elseStatements != null) ? ResultOperation.Ignore : resultOperation);

            MSA.Expression enterRescue = null, leaveRescue = null;
            var retryLabel = Ast.Label("retry");

            // make rescue clause:
            MSA.Expression transformedRescue;
            if (_rescueClauses != null) {
                // outer-most EH blocks sets and clears runtime flag RuntimeFlowControl.InTryRescue:
                if (gen.CurrentRescue == null) {
                    enterRescue = Methods.EnterRescue.OpCall(gen.CurrentScopeVariable);
                    leaveRescue = Methods.LeaveRescue.OpCall(gen.CurrentScopeVariable);
                } else {
                    enterRescue = leaveRescue = AstUtils.Empty();
                }

                gen.EnterRescueClause(retryingVariable, retryLabel);

                var handlers = new IfStatementTest[_rescueClauses.Count];
                for (int i = 0; i < handlers.Length; i++) {
                    handlers[i] = _rescueClauses[i].Transform(gen, resultOperation);
                }

                transformedRescue =
                    AstUtils.Try(
                        enterRescue,
                        AstUtils.If(handlers, Ast.Assign(exceptionRethrowVariable, AstUtils.Constant(true)))
                    ).Filter(unwinder = Ast.Parameter(typeof(EvalUnwinder), "#u"),
                        Ast.Equal(Ast.Field(unwinder, EvalUnwinder.ReasonField), AstUtils.Constant(BlockReturnReason.Retry)),

                        Ast.Block(
                            Ast.Assign(retryingVariable, AstUtils.Constant(true)),
                            Ast.Continue(retryLabel),
                            AstUtils.Empty()
                        )
                    );
                

                gen.LeaveRescueClause();

            } else {
                transformedRescue = Ast.Assign(exceptionRethrowVariable, AstUtils.Constant(true));
            }

            if (_elseStatements != null) {
                transformedElse = AstUtils.Unless(exceptionThrownVariable, transformedElse);
            }

            var result = Ast.Block(
                Ast.Label(retryLabel),
                AstUtils.Try(
                    Ast.Assign(exceptionThrownVariable, AstUtils.Constant(false)),
                    Ast.Assign(exceptionRethrowVariable, AstUtils.Constant(false)),
                    Ast.Assign(retryingVariable, AstUtils.Constant(false)),

                    // save exception (old_$! is not used unless there is a rescue clause):
                    (_rescueClauses == null) ? (MSA.Expression)AstUtils.Empty() :
                        Ast.Assign(oldExceptionVariable, Methods.GetCurrentException.OpCall(gen.CurrentScopeVariable)),

                    AstUtils.Try(
                        Ast.Block(transformedBody, AstUtils.Empty())
                    ).Filter(exceptionVariable = Ast.Parameter(typeof(Exception), "#e"), 
                        Methods.CanRescue.OpCall(gen.CurrentScopeVariable, exceptionVariable),

                        Ast.Assign(exceptionThrownVariable, AstUtils.Constant(true)),
                        transformedRescue,
                        AstUtils.Empty()
                    ).FinallyIf((_rescueClauses != null), 
                        // restore previous exception if the current one has been handled:
                        AstUtils.Unless(exceptionRethrowVariable,
                            Methods.SetCurrentException.OpCall(gen.CurrentScopeVariable, oldExceptionVariable)
                        ),
                        leaveRescue
                    ),

                    // unless (exception_thrown) do <else-statements> end
                    transformedElse,
                    AstUtils.Empty()
                ).FilterIf((_rescueClauses != null || _elseStatements != null),
                    exceptionVariable = Ast.Parameter(typeof(Exception), "#e"), 
                    Methods.CanRescue.OpCall(gen.CurrentScopeVariable, exceptionVariable),

                    Ast.Assign(exceptionRethrowVariable, AstUtils.Constant(true)),
                    AstUtils.Empty()
                ).FinallyWithJumps(
                    AstUtils.Unless(retryingVariable, transformedEnsure)
                )
            );

            return result;
        }