From: nobu.nokada@... Date: 2003-11-29T18:53:07+09:00 Subject: Re: re-raising an exception with the original backtrace Hi, At Sat, 29 Nov 2003 08:53:40 +0900, Joel VanderWerf wrote: > Ooh, that's nice. I thought 'raise' would just re-raise the _last_ > exception, but it's actually sensitive to the dynamic context. The > example below would fail in the former case, but since it succeeds, I > guess raise looks back down the stack and re-raises the first exception > it finds there? But I found that outer $! was preserved only in rescue clauses, in 1.6.8 and 1.8.1. > $ cat exception.rb > > class MyError < StandardError; end > > def foo > raise MyError > end > > def main > foo > rescue Exception => e > handler(e) > end > > class OtherError < StandardError; end def do_something_that_might_use_exceptions(retrying = false) > raise OtherError > rescue Exception => e retry if (retrying ^= true) > p e > false > end > > def handler(e) > ok = do_something_that_might_use_exceptions > if ok > return true > else > puts "backtrace:", e.backtrace, "---" > raise > end > end > > main $ ruby -v exception.rb ruby 1.8.1 (2003-11-28) [i686-linux] # backtrace: exception.rb:4:in `foo' exception.rb:8:in `main' exception.rb:32 --- exception.rb:28:in `handler': unhandled exception from exception.rb:10:in `main' from exception.rb:32 In other words, only changing $! in rescue clauses has no effect for outside. I suspect that rescue blocks should preserve $! value when no exception raised and $! should not be cleared at retry. Index: eval.c =================================================================== RCS file: /cvs/ruby/src/ruby/eval.c,v retrieving revision 1.598 diff -u -2 -p -r1.598 eval.c --- eval.c 27 Nov 2003 19:15:29 -0000 1.598 +++ eval.c 29 Nov 2003 09:48:56 -0000 @@ -2857,4 +2857,5 @@ rb_eval(self, n) PUSH_TAG(PROT_NONE); if ((state = EXEC_TAG()) == 0) { + retry_entry: result = rb_eval(self, node->nd_head); } @@ -2865,9 +2866,8 @@ rb_eval(self, n) else if (state == TAG_RETRY) { rescuing = state = 0; - e_info = ruby_errinfo = Qnil; - result = rb_eval(self, node->nd_head); + ruby_errinfo = e_info; + goto retry_entry; } else if (state != TAG_RAISE) { - ruby_errinfo = e_info; result = prot_tag->retval; } @@ -2883,5 +2883,4 @@ rb_eval(self, n) rescuing = 1; result = rb_eval(self, resq->nd_body); - ruby_errinfo = e_info; break; } @@ -2893,4 +2892,5 @@ rb_eval(self, n) } POP_TAG(); + if (state != TAG_RAISE) ruby_errinfo = e_info; if (state) { if (state == TAG_NEXT) prot_tag->retval = result; -- Nobu Nakada