예제 #1
0
 public static TryStatement TryCatchFinally(SourceSpan span, SourceLocation header, Statement body, CatchBlock[] handlers, Statement @finally)
 {
     return new TryStatement(
         span, header,
          body, CollectionUtils.ToReadOnlyCollection(handlers), @finally
     );
 }
예제 #2
0
        public Expression ToExpression()
        {
            //
            // We can't emit a real filter or fault because they don't
            // work in DynamicMethods. Instead we do a simple transformation:
            //   fault -> catch (Exception) { ...; rethrow }
            //   filter -> catch (ExceptionType) { if (!filter) rethrow; ... }
            //
            // Note that the filter transformation is not quite equivalent to
            // real CLR semantics, but it's what IronPython and IronRuby
            // expect. If we get CLR support we'll switch over to real filter
            // and fault blocks.
            //

            var handlers = new List <CatchBlock>(_catchBlocks);

            for (int i = 0, n = handlers.Count; i < n; i++)
            {
                CatchBlock handler = handlers[i];
                if (handler.Filter != null)
                {
                    handlers[i] = Expression.MakeCatchBlock(
                        handler.Test,
                        handler.Variable,
                        Expression.Condition(
                            handler.Filter,
                            handler.Body,
                            Expression.Rethrow(handler.Body.Type)
                            ),
                        null
                        );
                }
            }

            if (_fault != null)
            {
                ContractUtils.Requires(handlers.Count == 0, "fault cannot be used with catch or finally clauses");
                handlers.Add(Expression.Catch(typeof(Exception), Expression.Block(_fault, Expression.Rethrow(_try.Type))));
            }

            var result = Expression.MakeTry(null, _try, _finally, null, handlers);

            if (_enableJumpsFromFinally)
            {
                return(Utils.FinallyFlowControl(result));
            }
            return(result);
        }
예제 #3
0
        // TryStatement
        private Statement Rewrite(TryStatement node)
        {
            Statement body = RewriteStatement(node.Body);
            ReadOnlyCollection <CatchBlock> handlers = node.Handlers;

            CatchBlock[] clone = null;

            if (handlers != null)
            {
                for (int i = 0; i < handlers.Count; i++)
                {
                    CatchBlock handler  = handlers[i];
                    CatchBlock rhandler = Rewrite(handler);

                    if (((object)rhandler != (object)handler) && (clone == null))
                    {
                        clone = Clone(handlers, i);
                    }

                    if (clone != null)
                    {
                        clone[i] = rhandler;
                    }
                }
            }

            Statement @finally = RewriteStatement(node.FinallyStatement);

            if ((clone != null) ||
                ((object)body != (object)node.Body) ||
                ((object)@finally != (object)node.FinallyStatement))
            {
                if (clone != null)
                {
                    handlers = CollectionUtils.ToReadOnlyCollection(clone);
                }

                return(new TryStatement(node.Span, node.Header, body, handlers, @finally));
            }
            else
            {
                return(node);
            }
        }
예제 #4
0
        // This is copied from the base implementation.
        // Just want to make sure we disallow yield in filters
        protected override CatchBlock VisitCatchBlock(CatchBlock node)
        {
            ParameterExpression v = VisitAndConvert(node.Variable, "VisitCatchBlock");
            int        yields     = _yields.Count;
            Expression f          = Visit(node.Filter);

            if (yields != _yields.Count)
            {
                // No one needs this yet, and it's not clear what it should even do
                throw new NotSupportedException("yield in filter is not allowed");
            }
            Expression b = Visit(node.Body);

            if (v == node.Variable && b == node.Body && f == node.Filter)
            {
                return(node);
            }
            return(Expression.MakeCatchBlock(node.Test, v, b, f));
        }
예제 #5
0
        private CatchBlock[] VisitHandlers(TryExpression node, bool realCatch)
        {
            CatchBlock[] handlers = new CatchBlock[node.Handlers.Count];
            for (int i = 0; i < node.Handlers.Count; i++)
            {
                var handler = node.Handlers[i];

                ParameterExpression oldRethrow = _rethrow;
                try {
                    if (handler.Variable == null)
                    {
                        ParameterExpression rethrow = _rethrow = Expression.Parameter(handler.Test, "$exception");
                        handlers[i] = Expression.Catch(
                            rethrow,
                            TrackCatch(Visit(handler.Body), rethrow, realCatch),
                            Visit(handler.Filter)
                            );
                    }
                    else
                    {
                        ParameterExpression rethrow = _rethrow = Expression.Parameter(typeof(Exception), "$exception");
                        handlers[i] = Expression.Catch(
                            handler.Variable,
                            Expression.Block(
                                new[] { rethrow },
                                Expression.Assign(rethrow, handler.Variable),
                                TrackCatch(Visit(handler.Body), rethrow, realCatch)
                                ),
                            Visit(handler.Filter)
                            );
                    }
                } finally {
                    _rethrow = oldRethrow;
                }
            }

            return(handlers);
        }
예제 #6
0
        internal TargetLabel AddCatchYieldTarget(TargetLabel label, int index, int handler)
        {
            // Yields inside catch blocks are hoisted out of the catch.
            // If the catch block has a finally, though, it will get wrapped in
            // another try block, in which case the direct jump is not possible
            // and code must route through the top target.

            Debug.Assert(_handlers != null && handler < _handlers.Count);
            CatchBlock cb = _handlers[handler];

            cb.Yield      = true;
            _yieldInCatch = true;

            if (_finally != null)
            {
                AddYieldTarget(ref _catchYields, label, index);
                return(EnsureTopTarget());
            }
            else
            {
                return(label);
            }
        }
예제 #7
0
 public CatchRecord(Slot slot, CatchBlock block)
 {
     _slot  = slot;
     _block = block;
 }
예제 #8
0
        protected override Expression VisitTry(TryExpression node)
        {
            int startYields = _yields.Count;

            bool savedInTryWithFinally = _inTryWithFinally;

            if (node.Finally != null || node.Fault != null)
            {
                _inTryWithFinally = true;
            }
            Expression @try      = Visit(node.Body);
            int        tryYields = _yields.Count;

            IList <CatchBlock> handlers = Visit(node.Handlers, VisitCatchBlock);
            int catchYields             = _yields.Count;

            // push a new return label in case the finally block yields
            _returnLabels.Push(Expression.Label());
            // only one of these can be non-null
            Expression  @finally      = Visit(node.Finally);
            Expression  fault         = Visit(node.Fault);
            LabelTarget finallyReturn = _returnLabels.Pop();
            int         finallyYields = _yields.Count;

            _inTryWithFinally = savedInTryWithFinally;

            if (@try == node.Body &&
                handlers == node.Handlers &&
                @finally == node.Finally &&
                fault == node.Fault)
            {
                return(node);
            }

            // No yields, just return
            if (startYields == _yields.Count)
            {
                return(Expression.MakeTry(null, @try, @finally, fault, handlers));
            }

            if (fault != null && finallyYields != catchYields)
            {
                // No one needs this yet, and it's not clear how we should get back to
                // the fault
                throw new NotSupportedException("yield in fault block is not supported");
            }

            // If try has yields, we need to build a new try body that
            // dispatches to the yield labels
            var tryStart = Expression.Label();

            if (tryYields != startYields)
            {
                @try = Expression.Block(MakeYieldRouter(node.Body.Type, startYields, tryYields, tryStart), @try);
            }

            // Transform catches with yield to deferred handlers
            if (catchYields != tryYields)
            {
                var block = new List <Expression>();

                block.Add(MakeYieldRouter(node.Body.Type, tryYields, catchYields, tryStart));
                block.Add(null); // empty slot to fill in later

                for (int i = 0, n = handlers.Count; i < n; i++)
                {
                    CatchBlock c = handlers[i];

                    if (c == node.Handlers[i])
                    {
                        continue;
                    }

                    if (handlers.IsReadOnly)
                    {
                        handlers = handlers.ToArray();
                    }

                    // the variable that will be scoped to the catch block
                    var exceptionVar = Expression.Variable(c.Test, null);

                    // the variable that the catch block body will use to
                    // access the exception. We reuse the original variable if
                    // the catch block had one. It needs to be hoisted because
                    // the catch might contain yields.
                    var deferredVar = c.Variable ?? Expression.Variable(c.Test, null);
                    _vars.Add(deferredVar);

                    // We need to ensure that filters can access the exception
                    // variable
                    Expression filter = c.Filter;
                    if (filter != null && c.Variable != null)
                    {
                        filter = Expression.Block(new[] { c.Variable }, Expression.Assign(c.Variable, exceptionVar), filter);
                    }

                    // catch (ExceptionType exceptionVar) {
                    //     deferredVar = exceptionVar;
                    // }
                    handlers[i] = Expression.Catch(
                        exceptionVar,
                        Expression.Block(
                            Expression.Assign(deferredVar, exceptionVar),
                            Expression.Default(node.Body.Type)
                            ),
                        filter
                        );

                    // We need to rewrite rethrows into "throw deferredVar"
                    var catchBody = new RethrowRewriter {
                        Exception = deferredVar
                    }.Visit(c.Body);

                    // if (deferredVar != null) {
                    //     ... catch body ...
                    // }
                    block.Add(
                        Expression.Condition(
                            Expression.NotEqual(deferredVar, AstUtils.Constant(null, deferredVar.Type)),
                            catchBody,
                            Expression.Default(node.Body.Type)
                            )
                        );
                }

                block[1] = Expression.MakeTry(null, @try, null, null, new ReadOnlyCollection <CatchBlock>(handlers));
                @try     = Expression.Block(block);
                handlers = new CatchBlock[0]; // so we don't reuse these
            }

            if (finallyYields != catchYields)
            {
                // We need to add a catch block to save the exception, so we
                // can rethrow in case there is a yield in the finally. Also,
                // add logic for returning. It looks like this:
                //
                // try { ... } catch (Exception all) { saved = all; }
                // finally {
                //  if (_finallyReturnVar) goto finallyReturn;
                //   ...
                //   if (saved != null) throw saved;
                //   finallyReturn:
                // }
                // if (_finallyReturnVar) goto _return;

                // We need to add a catch(Exception), so if we have catches,
                // wrap them in a try
                if (handlers.Count > 0)
                {
                    @try     = Expression.MakeTry(null, @try, null, null, handlers);
                    handlers = new CatchBlock[0];
                }

                // NOTE: the order of these routers is important
                // The first call changes the labels to all point at "tryEnd",
                // so the second router will jump to "tryEnd"
                var        tryEnd          = Expression.Label();
                Expression inFinallyRouter = MakeYieldRouter(node.Body.Type, catchYields, finallyYields, tryEnd);
                Expression inTryRouter     = MakeYieldRouter(node.Body.Type, catchYields, finallyYields, tryStart);

                var all   = Expression.Variable(typeof(Exception), "e");
                var saved = Expression.Variable(typeof(Exception), "$saved$" + _temps.Count);
                _temps.Add(saved);
                @try = Expression.Block(
                    Expression.TryCatchFinally(
                        Expression.Block(
                            inTryRouter,
                            @try,
                            Expression.Assign(saved, AstUtils.Constant(null, saved.Type)),
                            Expression.Label(tryEnd)
                            ),
                        Expression.Block(
                            MakeSkipFinallyBlock(finallyReturn),
                            inFinallyRouter,
                            @finally,
                            Expression.Condition(
                                Expression.NotEqual(saved, AstUtils.Constant(null, saved.Type)),
                                Expression.Throw(saved),
                                Utils.Empty()
                                ),
                            Expression.Label(finallyReturn)
                            ),
                        Expression.Catch(all, Utils.Void(Expression.Assign(saved, all)))
                        ),
                    Expression.Condition(
                        Expression.Equal(_gotoRouter, AstUtils.Constant(GotoRouterYielding)),
                        Expression.Goto(_returnLabels.Peek()),
                        Utils.Empty()
                        )
                    );

                @finally = null;
            }
            else if (@finally != null)
            {
                // try or catch had a yield, modify finally so we can skip over it
                @finally = Expression.Block(
                    MakeSkipFinallyBlock(finallyReturn),
                    @finally,
                    Expression.Label(finallyReturn)
                    );
            }

            // Make the outer try, if needed
            if (handlers.Count > 0 || @finally != null || fault != null)
            {
                @try = Expression.MakeTry(null, @try, @finally, fault, handlers);
            }

            return(Expression.Block(Expression.Label(tryStart), @try));
        }
예제 #9
0
        protected override Expression VisitTry(TryExpression node)
        {
            int startYields = _yields.Count;

            bool savedInTryWithFinally = _inTryWithFinally;

            if (node.Finally != null || node.Fault != null)
            {
                _inTryWithFinally = true;
            }
            Expression @try      = Visit(node.Body);
            int        tryYields = _yields.Count;

            IList <CatchBlock> handlers = Visit(node.Handlers, VisitCatchBlock);
            int catchYields             = _yields.Count;

            // push a new return label in case the finally block yields
            _returnLabels.Push(Expression.Label());
            // only one of these can be non-null
            Expression  @finally      = Visit(node.Finally);
            Expression  fault         = Visit(node.Fault);
            LabelTarget finallyReturn = _returnLabels.Pop();
            int         finallyYields = _yields.Count;

            _inTryWithFinally = savedInTryWithFinally;

            if (@try == node.Body &&
                handlers == node.Handlers &&
                @finally == node.Finally &&
                fault == node.Fault)
            {
                return(node);
            }

            // No yields, just return
            if (startYields == _yields.Count)
            {
                return(Expression.MakeTry(@try, @finally, fault, handlers));
            }

            if (fault != null && finallyYields != catchYields)
            {
                // No one needs this yet, and it's not clear how we should get back to
                // the fault
                throw new NotSupportedException("yield in fault block is not supported");
            }

            // If try has yields, we need to build a new try body that
            // dispatches to the yield labels
            var tryStart = Expression.Label();

            if (tryYields != startYields)
            {
                @try = Expression.Block(MakeYieldRouter(startYields, tryYields, tryStart), @try);
            }

            // Transform catches with yield to deferred handlers
            if (catchYields != tryYields)
            {
                // Temps which are only needed temporarily, so they can go into
                // a transient scope (contents will be lost each yield)
                var temps = new List <ParameterExpression>();
                var block = new List <Expression>();

                block.Add(MakeYieldRouter(tryYields, catchYields, tryStart));
                block.Add(null); // empty slot to fill in later

                for (int i = 0, n = handlers.Count; i < n; i++)
                {
                    CatchBlock c = handlers[i];

                    if (c == node.Handlers[i])
                    {
                        continue;
                    }

                    if (handlers.IsReadOnly)
                    {
                        handlers = handlers.ToArray();
                    }

                    // TODO: when CatchBlock's variable is scoped properly, this
                    // implementation will need to be different
                    var deferredVar = Expression.Variable(c.Test, null);
                    temps.Add(deferredVar);
                    handlers[i] = Expression.Catch(deferredVar, Expression.Empty(), c.Filter);

                    // We need to rewrite rethrows into "throw deferredVar"
                    var catchBody = new RethrowRewriter {
                        Exception = deferredVar
                    }.Visit(c.Body);

                    if (c.Variable != null)
                    {
                        catchBody = Expression.Block(Expression.Assign(c.Variable, deferredVar), catchBody);
                    }
                    else
                    {
                        catchBody = Expression.Block(catchBody);
                    }

                    block.Add(
                        Expression.Condition(
                            Expression.NotEqual(deferredVar, Expression.Constant(null, deferredVar.Type)),
                            Expression.Void(catchBody),
                            Expression.Empty()
                            )
                        );
                }

                block[1] = Expression.MakeTry(@try, null, null, new ReadOnlyCollection <CatchBlock>(handlers));
                @try     = Expression.Block(temps, block);
                handlers = new CatchBlock[0]; // so we don't reuse these
            }

            if (finallyYields != catchYields)
            {
                // We need to add a catch block to save the exception, so we
                // can rethrow in case there is a yield in the finally. Also,
                // add logic for returning. It looks like this:
                // try { ... } catch (Exception e) {}
                // finally {
                //  if (_finallyReturnVar) goto finallyReturn;
                //   ...
                //   if (e != null) throw e;
                //   finallyReturn:
                // }
                // if (_finallyReturnVar) goto _return;

                // We need to add a catch(Exception), so if we have catches,
                // wrap them in a try
                if (handlers.Count > 0)
                {
                    @try     = Expression.MakeTry(@try, null, null, handlers);
                    handlers = new CatchBlock[0];
                }

                // NOTE: the order of these routers is important
                // The first call changes the labels to all point at "tryEnd",
                // so the second router will jump to "tryEnd"
                var        tryEnd          = Expression.Label();
                Expression inFinallyRouter = MakeYieldRouter(catchYields, finallyYields, tryEnd);
                Expression inTryRouter     = MakeYieldRouter(catchYields, finallyYields, tryStart);

                var exception = Expression.Variable(typeof(Exception), "$temp$" + _temps.Count);
                _temps.Add(exception);
                @try = Expression.Block(
                    Expression.TryCatchFinally(
                        Expression.Block(
                            inTryRouter,
                            @try,
                            Expression.Assign(exception, Expression.Constant(null, exception.Type)),
                            Expression.Label(tryEnd)
                            ),
                        Expression.Block(
                            MakeSkipFinallyBlock(finallyReturn),
                            inFinallyRouter,
                            @finally,
                            Expression.Condition(
                                Expression.NotEqual(exception, Expression.Constant(null, exception.Type)),
                                Expression.Throw(exception),
                                Expression.Empty()
                                ),
                            Expression.Label(finallyReturn)
                            ),
                        Expression.Catch(exception, Expression.Empty())
                        ),
                    Expression.Condition(
                        Expression.Equal(_gotoRouter, Expression.Constant(GotoRouterYielding)),
                        Expression.Goto(_returnLabels.Peek()),
                        Expression.Empty()
                        )
                    );

                @finally = null;
            }
            else if (@finally != null)
            {
                // try or catch had a yield, modify finally so we can skip over it
                @finally = Expression.Block(
                    MakeSkipFinallyBlock(finallyReturn),
                    @finally,
                    Expression.Label(finallyReturn)
                    );
            }

            // Make the outer try, if needed
            if (handlers.Count > 0 || @finally != null || fault != null)
            {
                @try = Expression.MakeTry(@try, @finally, fault, handlers);
            }

            return(Expression.Block(Expression.Label(tryStart), @try));
        }
예제 #10
0
 protected internal override bool Walk(CatchBlock node)
 {
     // CatchBlock is not required to have target variable
     if (node.Variable != null) {
         node.Ref = Reference(node.Variable);
     }
     return true;
 }
예제 #11
0
 public CatchRecord(Slot slot, CatchBlock block)
 {
     _slot = slot;
     _block = block;
 }
예제 #12
0
 private static void EmitSaveExceptionOrPop(CodeGen cg, CatchBlock cb)
 {
     if (cb.Variable != null) {
         Debug.Assert(cb.Slot != null);
         // If the variable is present, store the exception
         // in the variable.
         cb.Slot.EmitSet(cg);
     } else {
         // Otherwise, pop it off the stack.
         cg.Emit(OpCodes.Pop);
     }
 }
예제 #13
0
 // CatchBlock
 private void DefaultWalk(CatchBlock node)
 {
     if (Walk(node)) {
         WalkNode(node.Body);
     }
     PostWalk(node);
 }
예제 #14
0
 protected internal override bool Walk(CatchBlock node) {
     if (node.Variable != null) {
         node.Ref = GetOrMakeRef(node.Variable);
     }
     return true;
 }