From 534bd7d868b589ed1c70c181e1fa996611b15aea Mon Sep 17 00:00:00 2001 From: makoto kuwata Date: Mon, 27 Sep 2010 06:27:48 +0900 Subject: [PATCH 1/2] add Kernel#called_from() which is similar to caller() * See comment of rb_f_called_from() on vm_eval.c for details. --- vm.c | 23 +++++++++++++++++++++++ vm_eval.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 0 deletions(-) diff --git a/vm.c b/vm.c index cfc9312..b854f76 100644 --- a/vm.c +++ b/vm.c @@ -748,6 +748,29 @@ vm_backtrace_each(rb_thread_t *th, int lev, void (*init)(void *), rb_backtrace_i return TRUE; } +static VALUE +vm_backtrace_get_location(rb_thread_t *th, int lev) +{ + const rb_control_frame_t *limit_cfp = th->cfp; + const rb_control_frame_t *cfp = (void *)(th->stack + th->stack_size); + int line_no; + VALUE filename, name; + + cfp -= 2; + while (lev-- >= 0) { + if (++limit_cfp > cfp) { + return Qnil; + } + } + if (limit_cfp->iseq == NULL) { + return Qnil; + } + line_no = rb_vm_get_sourceline(limit_cfp); + filename = limit_cfp->iseq->filename; + name = limit_cfp->iseq->name; + return rb_ary_new3(3, filename, line_no ? INT2NUM(line_no) : Qnil, name); +} + static void vm_backtrace_alloc(void *arg) { diff --git a/vm_eval.c b/vm_eval.c index 4137189..90b6195 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -18,6 +18,7 @@ static inline VALUE vm_yield(rb_thread_t *th, int argc, const VALUE *argv); static inline VALUE vm_backtrace(rb_thread_t *th, int lev); static int vm_backtrace_each(rb_thread_t *th, int lev, void (*init)(void *), rb_backtrace_iter_func *iter, void *arg); static NODE *vm_cref_push(rb_thread_t *th, VALUE klass, int noex, rb_block_t *blockptr); +static VALUE vm_backtrace_get_location(rb_thread_t *th, int lev); static VALUE vm_exec(rb_thread_t *th); static void vm_set_eval_stack(rb_thread_t * th, VALUE iseqval, const NODE *cref); static int vm_collect_local_variables_in_heap(rb_thread_t *th, VALUE *dfp, VALUE ary); @@ -1583,6 +1584,50 @@ rb_f_caller(int argc, VALUE *argv) return vm_backtrace(GET_THREAD(), lev); } +/* + * call-seq: + * called_from(start=1) -> array or nil + * + * Returns file name, line number, and method name of the stack. + * The optional _start_ parameter represents the number of stack + * entries to skip. + * + * Returns +nil+ if _start_ is greater than the size of + * current execution stack. + * + * Raises ArgumentError if _start_ is negative value. + * + * # example.rb + * 1: def f1() + * 2: f2() + * 3: end + * 4: def f2() + * 5: f3() + * 6: end + * 7: def f3() + * 8: p called_from() #=> ["example.rb", 5, "f2"] + * 9: p called_from(0) #=> ["example.rb", 9, "f3"] + * 10: p called_from(1) #=> ["example.rb", 5, "f2"] + * 11: p called_from(2) #=> ["example.rb", 2, "f1"] + * 12: p called_from(3) #=> ["example.rb", 15, "
"] + * 13: p called_from(4) #=> nil + * 14: end + * 15: f1() + */ +static VALUE +rb_f_called_from(int argc, VALUE *argv) +{ + VALUE level; + int lev; + VALUE ary = Qnil; + + rb_scan_args(argc, argv, "01", &level); + lev = NIL_P(level) ? 1 : NUM2INT(level); + if (lev < 0) rb_raise(rb_eArgError, "negative level (%d)", lev); + + return vm_backtrace_get_location(GET_THREAD(), lev); +} + static int print_backtrace(void *arg, VALUE file, int line, VALUE method) { @@ -1774,5 +1819,6 @@ Init_vm_eval(void) rb_define_method(rb_cModule, "class_eval", rb_mod_module_eval, -1); rb_define_global_function("caller", rb_f_caller, -1); + rb_define_global_function("called_from", rb_f_called_from, -1); } -- 1.7.2.3