diff --git a/ChangeLog b/ChangeLog index 8591c4ef81..35e644f495 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +Fri Dec 13 13:25:30 2013 Koichi Sasada + + * error.c: add Exception#backtrace_locations. + Now, there are no setter and independent from Exception#backtrace. + [Feature #8960] + + * eval.c (setup_exception): set backtrace locations for `bt_location' + special attribute. + + * vm_backtrace.c (rb_backtrace_to_location_ary): added. + + * internal.h: ditto. + + * test/ruby/test_backtrace.rb: add a test for + Exception#backtrace_locations. + Fri Dec 13 12:01:07 2013 Koichi Sasada * gc.c (garbage_collect_body): use rb_bug() and explicit error message diff --git a/error.c b/error.c index ac7b632751..b9d8d52809 100644 --- a/error.c +++ b/error.c @@ -736,6 +736,30 @@ exc_backtrace(VALUE exc) return obj; } +/* + * call-seq: + * exception.backtrace_locations -> array + * + * Returns any backtrace associated with the exception. This method is + * similar to Exception#backtrace, but the backtrace is an array of + * Thread::Backtrace::Location. + * + * Now, this method is not affected by Exception#set_backtrace(). + */ +static VALUE +exc_backtrace_locations(VALUE exc) +{ + ID bt_locations; + VALUE obj; + + CONST_ID(bt_locations, "bt_locations"); + obj = rb_attr_get(exc, bt_locations); + if (!NIL_P(obj)) { + obj = rb_backtrace_to_location_ary(obj); + } + return obj; +} + VALUE rb_check_backtrace(VALUE bt) { @@ -1749,6 +1773,7 @@ Init_Exception(void) rb_define_method(rb_eException, "message", exc_message, 0); rb_define_method(rb_eException, "inspect", exc_inspect, 0); rb_define_method(rb_eException, "backtrace", exc_backtrace, 0); + rb_define_method(rb_eException, "backtrace_locations", exc_backtrace_locations, 0); rb_define_method(rb_eException, "set_backtrace", exc_set_backtrace, 1); rb_define_method(rb_eException, "cause", exc_cause, 0); diff --git a/eval.c b/eval.c index c92d05dc19..2882c57295 100644 --- a/eval.c +++ b/eval.c @@ -493,10 +493,12 @@ setup_exception(rb_thread_t *th, int tag, volatile VALUE mesg) if (OBJ_FROZEN(mesg)) { mesg = rb_obj_dup(mesg); } + rb_iv_set(mesg, "bt_locations", at); set_backtrace(mesg, at); } } } + if (!NIL_P(mesg)) { th->errinfo = mesg; } diff --git a/internal.h b/internal.h index 51e626a493..344963243a 100644 --- a/internal.h +++ b/internal.h @@ -800,6 +800,7 @@ VALUE rb_make_backtrace(void); void rb_backtrace_print_as_bugreport(void); int rb_backtrace_p(VALUE obj); VALUE rb_backtrace_to_str_ary(VALUE obj); +VALUE rb_backtrace_to_location_ary(VALUE obj); void rb_backtrace_print_to(VALUE output); VALUE rb_vm_backtrace_object(void); diff --git a/test/ruby/test_backtrace.rb b/test/ruby/test_backtrace.rb index 91dec4a943..aded544dcf 100644 --- a/test/ruby/test_backtrace.rb +++ b/test/ruby/test_backtrace.rb @@ -14,6 +14,36 @@ class TestBacktrace < Test::Unit::TestCase assert_match(/.+:\d+:.+/, bt[0]) end + def helper_test_exception_backtrace_locations + raise + end + + def test_exception_backtrace_locations + bt = Fiber.new{ + begin + raise + rescue => e + e.backtrace_locations + end + }.resume + assert_equal(1, bt.size) + assert_match(/.+:\d+:.+/, bt[0].to_s) + + bt = Fiber.new{ + begin + begin + helper_test_exception_backtrace_locations + rescue + raise + end + rescue => e + e.backtrace_locations + end + }.resume + assert_equal(2, bt.size) + assert_match(/helper_test_exception_backtrace_locations/, bt[0].to_s) + end + def test_caller_lev cs = [] Fiber.new{ diff --git a/vm_backtrace.c b/vm_backtrace.c index 5b05b04050..f57458e54a 100644 --- a/vm_backtrace.c +++ b/vm_backtrace.c @@ -372,6 +372,7 @@ typedef struct rb_backtrace_struct { rb_backtrace_location_t *backtrace_base; int backtrace_size; VALUE strary; + VALUE locary; } rb_backtrace_t; static void @@ -383,8 +384,9 @@ backtrace_mark(void *ptr) for (i=0; ibacktrace[i]); - rb_gc_mark(bt->strary); } + rb_gc_mark(bt->strary); + rb_gc_mark(bt->locary); } } @@ -631,6 +633,18 @@ backtrace_to_location_ary(VALUE self, long lev, long n) return r; } +VALUE +rb_backtrace_to_location_ary(VALUE self) +{ + rb_backtrace_t *bt; + GetCoreDataFromValue(self, rb_backtrace_t, bt); + + if (!bt->locary) { + bt->locary = backtrace_to_location_ary(self, 0, 0); + } + return bt->locary; +} + static VALUE backtrace_dump_data(VALUE self) {