aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog16
-rw-r--r--error.c25
-rw-r--r--eval.c2
-rw-r--r--internal.h1
-rw-r--r--test/ruby/test_backtrace.rb30
-rw-r--r--vm_backtrace.c16
6 files changed, 89 insertions, 1 deletions
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 <ko1@atdot.net>
+
+ * 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 <ko1@atdot.net>
* 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; i<s; i++) {
location_mark_entry(&bt->backtrace[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)
{