1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

* vm.c: add RubyVM::Backtrace object (btobj).

Backtrace information contains an array consists of location
  information for each frames by string.
  RubyVM::Backtrace object is lightweight backtrace information,
  which contains complete information to generate traditional style
  backtrace (an array of strings) with faster generation.
  If someone accesses to backtrace information via
  Exception#backtrace, then convert a RubyVM::Backtrace object to
  traditonal style backtrace.
  This change causes incompatibility on marshal dumpped binary
  of Exception.  If you have any trouble on it, please tell us
  before Ruby 2.0 release.
  Note that RubyVM::Backtrace object should not expose Ruby level.
* error.c, eval.c, vm_eval.c: ditto.
* internal.h: ditto.
* eval_error.c: fix to skip "set_backtrace" method invocation in
  creating an exception object if it call a normal set_backtrace
  method (defined by core).
* test/ruby/test_settracefunc.rb: fix for above change.
* vm_method.c (rb_method_defined_by): added.  This function
  checks that the given object responds with the given method
  by the given cfunc.
* benchmark/bm_vm2_raise1.rb, benchmark/bm_vm2_raise2.rb:
  add to measure exception creation speed. raise1 create
  exception objects from shallow stack frame.  raise2 create
  exception objects from deep stack frame.



git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@35769 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
ko1 2012-05-24 06:09:23 +00:00
parent bd5c7507ec
commit 3dcebce523
11 changed files with 426 additions and 28 deletions

View file

@ -1,3 +1,38 @@
Thu May 24 14:30:13 2012 Koichi Sasada <ko1@atdot.net>
* vm.c: add RubyVM::Backtrace object (btobj).
Backtrace information contains an array consists of location
information for each frames by string.
RubyVM::Backtrace object is lightweight backtrace information,
which contains complete information to generate traditional style
backtrace (an array of strings) with faster generation.
If someone accesses to backtrace information via
Exception#backtrace, then convert a RubyVM::Backtrace object to
traditonal style backtrace.
This change causes incompatibility on marshal dumpped binary
of Exception. If you have any trouble on it, please tell us
before Ruby 2.0 release.
Note that RubyVM::Backtrace object should not expose Ruby level.
* error.c, eval.c, vm_eval.c: ditto.
* internal.h: ditto.
* eval_error.c: fix to skip "set_backtrace" method invocation in
creating an exception object if it call a normal set_backtrace
method (defined by core).
* test/ruby/test_settracefunc.rb: fix for above change.
* vm_method.c (rb_method_defined_by): added. This function
checks that the given object responds with the given method
by the given cfunc.
* benchmark/bm_vm2_raise1.rb, benchmark/bm_vm2_raise2.rb:
add to measure exception creation speed. raise1 create
exception objects from shallow stack frame. raise2 create
exception objects from deep stack frame.
Thu May 24 12:07:46 2012 Nobuyoshi Nakada <nobu@ruby-lang.org> Thu May 24 12:07:46 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
* io.c (io_strip_bom): check EOF. [Bug #6487][ruby-core:45203] * io.c (io_strip_bom): check EOF. [Bug #6487][ruby-core:45203]

View file

@ -0,0 +1,18 @@
def rec n
if n > 0
rec n-1
else
raise
end
end
i=0
while i<6_000_000 # benchmark loop 2
i+=1
begin
rec 1
rescue
# ignore
end
end

View file

@ -0,0 +1,18 @@
def rec n
if n > 0
rec n-1
else
raise
end
end
i=0
while i<6_000_000 # benchmark loop 2
i+=1
begin
rec 10
rescue
# ignore
end
end

17
error.c
View file

@ -673,9 +673,17 @@ static VALUE
exc_backtrace(VALUE exc) exc_backtrace(VALUE exc)
{ {
ID bt; ID bt;
VALUE obj;
CONST_ID(bt, "bt"); CONST_ID(bt, "bt");
return rb_attr_get(exc, bt); obj = rb_attr_get(exc, bt);
if (rb_backtrace_p(obj)) {
obj = rb_backtrace_to_str_ary(obj);
/* rb_iv_set(exc, "bt", obj); */
}
return obj;
} }
VALUE VALUE
@ -686,6 +694,7 @@ rb_check_backtrace(VALUE bt)
if (!NIL_P(bt)) { if (!NIL_P(bt)) {
if (RB_TYPE_P(bt, T_STRING)) return rb_ary_new3(1, bt); if (RB_TYPE_P(bt, T_STRING)) return rb_ary_new3(1, bt);
if (rb_backtrace_p(bt)) return bt;
if (!RB_TYPE_P(bt, T_ARRAY)) { if (!RB_TYPE_P(bt, T_ARRAY)) {
rb_raise(rb_eTypeError, err); rb_raise(rb_eTypeError, err);
} }
@ -708,8 +717,8 @@ rb_check_backtrace(VALUE bt)
* *
*/ */
static VALUE VALUE
exc_set_backtrace(VALUE exc, VALUE bt) rb_exc_set_backtrace(VALUE exc, VALUE bt)
{ {
return rb_iv_set(exc, "bt", rb_check_backtrace(bt)); return rb_iv_set(exc, "bt", rb_check_backtrace(bt));
} }
@ -1669,7 +1678,7 @@ Init_Exception(void)
rb_define_method(rb_eException, "message", exc_message, 0); rb_define_method(rb_eException, "message", exc_message, 0);
rb_define_method(rb_eException, "inspect", exc_inspect, 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", exc_backtrace, 0);
rb_define_method(rb_eException, "set_backtrace", exc_set_backtrace, 1); rb_define_method(rb_eException, "set_backtrace", rb_exc_set_backtrace, 1);
rb_eSystemExit = rb_define_class("SystemExit", rb_eException); rb_eSystemExit = rb_define_class("SystemExit", rb_eException);
rb_define_method(rb_eSystemExit, "initialize", exit_initialize, -1); rb_define_method(rb_eSystemExit, "initialize", exit_initialize, -1);

2
eval.c
View file

@ -387,7 +387,7 @@ setup_exception(rb_thread_t *th, int tag, volatile VALUE mesg)
else { else {
at = get_backtrace(mesg); at = get_backtrace(mesg);
if (NIL_P(at)) { if (NIL_P(at)) {
at = rb_make_backtrace(); at = rb_vm_backtrace_object();
if (OBJ_FROZEN(mesg)) { if (OBJ_FROZEN(mesg)) {
mesg = rb_obj_dup(mesg); mesg = rb_obj_dup(mesg);
} }

View file

@ -58,6 +58,17 @@ rb_get_backtrace(VALUE info)
static void static void
set_backtrace(VALUE info, VALUE bt) set_backtrace(VALUE info, VALUE bt)
{ {
ID set_backtrace = rb_intern("set_backtrace");
if (rb_backtrace_p(bt)) {
if (rb_method_defined_by(info, set_backtrace, rb_exc_set_backtrace)) {
rb_exc_set_backtrace(info, bt);
return;
}
else {
bt = rb_backtrace_to_str_ary(bt);
}
}
rb_funcall(info, rb_intern("set_backtrace"), 1, bt); rb_funcall(info, rb_intern("set_backtrace"), 1, bt);
} }

View file

@ -83,6 +83,7 @@ void rb_gc_mark_encodings(void);
NORETURN(PRINTF_ARGS(void rb_compile_bug(const char*, int, const char*, ...), 3, 4)); NORETURN(PRINTF_ARGS(void rb_compile_bug(const char*, int, const char*, ...), 3, 4));
VALUE rb_check_backtrace(VALUE); VALUE rb_check_backtrace(VALUE);
NORETURN(void rb_async_bug_errno(const char *,int)); NORETURN(void rb_async_bug_errno(const char *,int));
VALUE rb_exc_set_backtrace(VALUE exc, VALUE bt);
/* eval_error.c */ /* eval_error.c */
void ruby_error_print(void); void ruby_error_print(void);
@ -207,6 +208,9 @@ void rb_vm_inc_const_missing_count(void);
void rb_thread_mark(void *th); void rb_thread_mark(void *th);
const void **rb_vm_get_insns_address_table(void); const void **rb_vm_get_insns_address_table(void);
VALUE rb_sourcefilename(void); VALUE rb_sourcefilename(void);
int rb_backtrace_p(VALUE obj);
VALUE rb_backtrace_to_str_ary(VALUE obj);
VALUE rb_vm_backtrace_object();
/* vm_dump.c */ /* vm_dump.c */
void rb_vm_bugreport(void); void rb_vm_bugreport(void);
@ -217,6 +221,7 @@ VALUE rb_current_realfilepath(void);
/* vm_method.c */ /* vm_method.c */
void Init_eval_method(void); void Init_eval_method(void);
int rb_method_defined_by(VALUE obj, ID mid, VALUE (*cfunc)(ANYARGS));
/* miniprelude.c, prelude.c */ /* miniprelude.c, prelude.c */
void Init_prelude(void); void Init_prelude(void);

View file

@ -250,10 +250,6 @@ class TestSetTraceFunc < Test::Unit::TestCase
events.shift) events.shift)
assert_equal(["c-return", 5, :backtrace, Exception], assert_equal(["c-return", 5, :backtrace, Exception],
events.shift) events.shift)
assert_equal(["c-call", 5, :set_backtrace, Exception],
events.shift)
assert_equal(["c-return", 5, :set_backtrace, Exception],
events.shift)
assert_equal(["raise", 5, :test_raise, TestSetTraceFunc], assert_equal(["raise", 5, :test_raise, TestSetTraceFunc],
events.shift) events.shift)
assert_equal(["c-return", 5, :raise, Kernel], assert_equal(["c-return", 5, :raise, Kernel],

305
vm.c
View file

@ -36,14 +36,16 @@ VALUE rb_cRubyVM;
VALUE rb_cThread; VALUE rb_cThread;
VALUE rb_cEnv; VALUE rb_cEnv;
VALUE rb_mRubyVMFrozenCore; VALUE rb_mRubyVMFrozenCore;
VALUE rb_cBacktrace;
VALUE rb_cFrameInfo;
VALUE ruby_vm_const_missing_count = 0; VALUE ruby_vm_const_missing_count = 0;
char ruby_vm_redefined_flag[BOP_LAST_]; char ruby_vm_redefined_flag[BOP_LAST_];
rb_thread_t *ruby_current_thread = 0; rb_thread_t *ruby_current_thread = 0;
rb_vm_t *ruby_current_vm = 0; rb_vm_t *ruby_current_vm = 0;
extern VALUE ruby_engine_name;
static void thread_free(void *ptr); static void thread_free(void *ptr);
void vm_analysis_operand(int insn, int n, VALUE op); void vm_analysis_operand(int insn, int n, VALUE op);
@ -731,6 +733,12 @@ rb_lastline_set(VALUE val)
/* backtrace */ /* backtrace */
inline static int
calc_line_no(const rb_iseq_t *iseq, VALUE *pc)
{
return rb_iseq_line_no(iseq, pc - iseq->iseq_encoded);
}
int int
rb_vm_get_sourceline(const rb_control_frame_t *cfp) rb_vm_get_sourceline(const rb_control_frame_t *cfp)
{ {
@ -738,22 +746,289 @@ rb_vm_get_sourceline(const rb_control_frame_t *cfp)
const rb_iseq_t *iseq = cfp->iseq; const rb_iseq_t *iseq = cfp->iseq;
if (RUBY_VM_NORMAL_ISEQ_P(iseq)) { if (RUBY_VM_NORMAL_ISEQ_P(iseq)) {
size_t pos = cfp->pc - cfp->iseq->iseq_encoded; line_no = calc_line_no(cfp->iseq, cfp->pc);
line_no = rb_iseq_line_no(iseq, pos);
} }
return line_no; return line_no;
} }
typedef struct rb_frame_info_struct {
enum FRAME_INFO_TYPE {
FRAME_INFO_TYPE_ISEQ = 1,
FRAME_INFO_TYPE_CFUNC,
FRAME_INFO_TYPE_IFUNC,
} type;
union {
struct {
rb_iseq_t *iseq;
VALUE *pc;
} iseq;
struct {
ID mid;
} cfunc;
} body;
} rb_frame_info_t;
static void
frame_info_mark(void *ptr)
{
if (ptr) {
rb_frame_info_t *fi = (rb_frame_info_t *)ptr;
switch (fi->type) {
case FRAME_INFO_TYPE_ISEQ:
rb_gc_mark(fi->body.iseq.iseq->self);
break;
case FRAME_INFO_TYPE_CFUNC:
case FRAME_INFO_TYPE_IFUNC:
default:
break;
}
}
}
static VALUE
frame_info_format(VALUE file, VALUE line_no, VALUE name)
{
if (line_no != INT2FIX(0)) {
return rb_enc_sprintf(rb_enc_compatible(file, name), "%s:%d:in `%s'",
RSTRING_PTR(file), FIX2INT(line_no), RSTRING_PTR(name));
}
else {
return rb_enc_sprintf(rb_enc_compatible(file, name), "%s:in `%s'",
RSTRING_PTR(file), RSTRING_PTR(name));
}
}
static VALUE
frame_info_to_str_override(rb_frame_info_t *fi, VALUE *args)
{
switch (fi->type) {
case FRAME_INFO_TYPE_ISEQ:
args[0] = fi->body.iseq.iseq->location.filename;
args[1] = INT2FIX(calc_line_no(fi->body.iseq.iseq, fi->body.iseq.pc));
args[2] = fi->body.iseq.iseq->location.name;
break;
case FRAME_INFO_TYPE_CFUNC:
args[2] = rb_id2str(fi->body.cfunc.mid);
break;
case FRAME_INFO_TYPE_IFUNC:
default:
rb_bug("frame_info_to_str_overwrite: unreachable");
}
return frame_info_format(args[0], args[1], args[2]);
}
typedef struct rb_backtrace_struct {
rb_frame_info_t *backtrace;
int backtrace_size;
VALUE str;
} rb_backtrace_t;
static void
backtrace_mark(void *ptr)
{
if (ptr) {
rb_backtrace_t *bt = (rb_backtrace_t *)ptr;
int i, s = bt->backtrace_size;
for (i=0; i<s; i++) {
frame_info_mark(&bt->backtrace[i]);
rb_gc_mark(bt->str);
}
}
}
static void
backtrace_free(void *ptr)
{
if (ptr) {
rb_backtrace_t *bt = (rb_backtrace_t *)ptr;
if (bt->backtrace) ruby_xfree(bt->backtrace);
ruby_xfree(bt);
}
}
static size_t
backtrace_memsize(const void *ptr)
{
rb_backtrace_t *bt = (rb_backtrace_t *)ptr;
return sizeof(rb_backtrace_t) + sizeof(rb_frame_info_t) * bt->backtrace_size;
}
static const rb_data_type_t backtrace_data_type = {
"backtrace",
{backtrace_mark, backtrace_free, backtrace_memsize,},
};
int
rb_backtrace_p(VALUE obj)
{
if (TYPE(obj) == T_DATA && RTYPEDDATA_P(obj) && RTYPEDDATA_TYPE(obj) == &backtrace_data_type) {
return 1;
}
else {
return 0;
}
}
static VALUE
backtrace_alloc(VALUE klass)
{
rb_backtrace_t *bt;
VALUE obj = TypedData_Make_Struct(klass, rb_backtrace_t, &backtrace_data_type, bt);
bt->backtrace = 0;
bt->backtrace_size = 0;
bt->str = 0;
return obj;
}
static VALUE
backtrace_object(rb_thread_t *th, int lev, int n)
{
VALUE btobj = backtrace_alloc(rb_cBacktrace);
rb_backtrace_t *bt;
rb_control_frame_t *last_cfp = th->cfp;
rb_control_frame_t *start_cfp = RUBY_VM_END_CONTROL_FRAME(th);
rb_control_frame_t *cfp;
int size, i, j;
start_cfp = RUBY_VM_NEXT_CONTROL_FRAME(
RUBY_VM_NEXT_CONTROL_FRAME(
RUBY_VM_NEXT_CONTROL_FRAME(start_cfp))); /* skip top frames */
size = (start_cfp - last_cfp) + 1;
if (n <= 0) {
n = size + n;
if (n < 0) {
n = 0;
}
}
if (lev < 0) {
lev = 0;
}
GetCoreDataFromValue(btobj, rb_backtrace_t, bt);
bt->backtrace = ruby_xmalloc(sizeof(rb_frame_info_t) * n);
bt->backtrace_size = n;
for (i=lev, j=0, cfp = start_cfp; i<size && j<n; i++, cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
rb_frame_info_t *fi = &bt->backtrace[j];
if (cfp->iseq) {
if (cfp->pc) {
fi->type = FRAME_INFO_TYPE_ISEQ;
fi->body.iseq.iseq = cfp->iseq;
fi->body.iseq.pc = cfp->pc;
j++;
}
}
else if (RUBYVM_CFUNC_FRAME_P(cfp)) {
ID mid = cfp->me->def ? cfp->me->def->original_id : cfp->me->called_id;
if (mid != ID_ALLOCATOR) {
fi->type = FRAME_INFO_TYPE_CFUNC;
fi->body.cfunc.mid = mid;
j++;
}
}
}
if (j > 0) {
bt->backtrace_size = j; /* TODO: realloc? */
return btobj;
}
else {
/* gc will free object */
return Qnil;
}
}
static VALUE
backtreace_collect(rb_backtrace_t *bt, VALUE (*func)(rb_frame_info_t *, VALUE *))
{
VALUE btary;
int i;
VALUE args[3];
rb_thread_t *th = GET_THREAD();
btary = rb_ary_new2(bt->backtrace_size);
rb_ary_store(btary, bt->backtrace_size-1, Qnil); /* create places */
args[0] = th->vm->progname ? th->vm->progname : ruby_engine_name;;
args[1] = INT2FIX(0);
args[2] = Qnil;
for (i=0; i<bt->backtrace_size; i++) {
rb_frame_info_t *fi = &bt->backtrace[i];
RARRAY_PTR(btary)[bt->backtrace_size - i - 1] = func(fi, args);
}
return btary;
}
VALUE
rb_backtrace_to_str_ary(VALUE self)
{
rb_backtrace_t *bt;
GetCoreDataFromValue(self, rb_backtrace_t, bt);
if (bt->str) {
return bt->str;
}
else {
bt->str = backtreace_collect(bt, frame_info_to_str_override);
return bt->str;
}
}
#if 0
static VALUE
rb_backtrace_to_frame_ary(VALUE self)
{
return backtreace_collect(self, frame_info_create);
}
static VALUE
vm_backtrace_frame_ary(rb_thread_t *th, int lev, int n)
{
return rb_backtrace_to_frame_ary(vm_backtrace_create(th, lev, n));
}
#endif
static VALUE
backtrace_dump_data(VALUE self)
{
VALUE str = rb_backtrace_to_str_ary(self);
return str;
}
static VALUE
backtrace_load_data(VALUE self, VALUE str)
{
rb_backtrace_t *bt;
GetCoreDataFromValue(self, rb_backtrace_t, bt);
bt->str = str;
return self;
}
/* old style backtrace for compatibility */
static int static int
vm_backtrace_each(rb_thread_t *th, int lev, void (*init)(void *), rb_backtrace_iter_func *iter, void *arg) vm_backtrace_each(rb_thread_t *th, int lev, int n, void (*init)(void *), rb_backtrace_iter_func *iter, void *arg)
{ {
const rb_control_frame_t *limit_cfp = th->cfp; const rb_control_frame_t *limit_cfp = th->cfp;
const rb_control_frame_t *cfp = (void *)(th->stack + th->stack_size); const rb_control_frame_t *cfp = (void *)(th->stack + th->stack_size);
VALUE file = Qnil; VALUE file = Qnil;
int line_no = 0; int line_no = 0;
if (n <= 0) {
n = cfp - limit_cfp;
}
cfp -= 2; cfp -= 2;
while (lev-- >= 0) { while (lev-- >= 0 && n > 0) {
if (++limit_cfp > cfp) { if (++limit_cfp > cfp) {
return FALSE; return FALSE;
} }
@ -761,20 +1036,21 @@ vm_backtrace_each(rb_thread_t *th, int lev, void (*init)(void *), rb_backtrace_i
if (init) (*init)(arg); if (init) (*init)(arg);
limit_cfp = RUBY_VM_NEXT_CONTROL_FRAME(limit_cfp); limit_cfp = RUBY_VM_NEXT_CONTROL_FRAME(limit_cfp);
if (th->vm->progname) file = th->vm->progname; if (th->vm->progname) file = th->vm->progname;
while (cfp > limit_cfp) { while (cfp > limit_cfp && n>0) {
if (cfp->iseq != 0) { if (cfp->iseq != 0) {
if (cfp->pc != 0) { if (cfp->pc != 0) {
rb_iseq_t *iseq = cfp->iseq; rb_iseq_t *iseq = cfp->iseq;
line_no = rb_vm_get_sourceline(cfp); line_no = rb_vm_get_sourceline(cfp);
file = iseq->location.filename; file = iseq->location.filename;
n--;
if ((*iter)(arg, file, line_no, iseq->location.name)) break; if ((*iter)(arg, file, line_no, iseq->location.name)) break;
} }
} }
else if (RUBYVM_CFUNC_FRAME_P(cfp)) { else if (RUBYVM_CFUNC_FRAME_P(cfp)) {
ID id; ID id;
extern VALUE ruby_engine_name;
n--;
if (NIL_P(file)) file = ruby_engine_name; if (NIL_P(file)) file = ruby_engine_name;
if (cfp->me->def) if (cfp->me->def)
id = cfp->me->def->original_id; id = cfp->me->def->original_id;
@ -813,15 +1089,15 @@ vm_backtrace_push(void *arg, VALUE file, int line_no, VALUE name)
return 0; return 0;
} }
static inline VALUE static VALUE
vm_backtrace(rb_thread_t *th, int lev) vm_backtrace_str_ary(rb_thread_t *th, int lev, int n)
{ {
VALUE ary = 0; VALUE ary = 0;
if (lev < 0) { if (lev < 0) {
ary = rb_ary_new(); ary = rb_ary_new();
} }
vm_backtrace_each(th, lev, vm_backtrace_alloc, vm_backtrace_push, &ary); vm_backtrace_each(th, lev, 0, vm_backtrace_alloc, vm_backtrace_push, &ary);
if (!ary) return Qnil; if (!ary) return Qnil;
return rb_ary_reverse(ary); return rb_ary_reverse(ary);
} }
@ -2172,6 +2448,13 @@ Init_VM(void)
rb_cThread = rb_define_class("Thread", rb_cObject); rb_cThread = rb_define_class("Thread", rb_cObject);
rb_undef_alloc_func(rb_cThread); rb_undef_alloc_func(rb_cThread);
/* ::RubyVM::Backtrace */
rb_cBacktrace = rb_define_class_under(rb_cRubyVM, "Backtrace", rb_cObject);
rb_define_alloc_func(rb_cBacktrace, backtrace_alloc);
rb_undef_method(CLASS_OF(rb_cFrameInfo), "new");
rb_define_method(rb_cBacktrace, "_dump_data", backtrace_dump_data, 0);
rb_define_method(rb_cBacktrace, "_load_data", backtrace_load_data, 1);
/* ::RubyVM::USAGE_ANALYSIS_* */ /* ::RubyVM::USAGE_ANALYSIS_* */
rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_INSN", rb_hash_new()); rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_INSN", rb_hash_new());
rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_REGS", rb_hash_new()); rb_define_const(rb_cRubyVM, "USAGE_ANALYSIS_REGS", rb_hash_new());

View file

@ -15,13 +15,15 @@ static inline VALUE method_missing(VALUE obj, ID id, int argc, const VALUE *argv
static inline VALUE rb_vm_set_finish_env(rb_thread_t * th); static inline VALUE rb_vm_set_finish_env(rb_thread_t * th);
static inline VALUE vm_yield_with_cref(rb_thread_t *th, int argc, const VALUE *argv, const NODE *cref); static inline VALUE vm_yield_with_cref(rb_thread_t *th, int argc, const VALUE *argv, const NODE *cref);
static inline VALUE vm_yield(rb_thread_t *th, int argc, const VALUE *argv); 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 NODE *vm_cref_push(rb_thread_t *th, VALUE klass, int noex, rb_block_t *blockptr);
static VALUE vm_exec(rb_thread_t *th); static VALUE vm_exec(rb_thread_t *th);
static void vm_set_eval_stack(rb_thread_t * th, VALUE iseqval, const NODE *cref); 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); static int vm_collect_local_variables_in_heap(rb_thread_t *th, VALUE *dfp, VALUE ary);
static int vm_backtrace_each(rb_thread_t *th, int lev, int n, void (*init)(void *), rb_backtrace_iter_func *iter, void *arg);
static VALUE backtrace_object(rb_thread_t *th, int lev, int n);
static VALUE vm_backtrace_str_ary(rb_thread_t *th, int lev, int n);
typedef enum call_type { typedef enum call_type {
CALL_PUBLIC, CALL_PUBLIC,
CALL_FCALL, CALL_FCALL,
@ -1085,7 +1087,7 @@ eval_string_with_cref(VALUE self, VALUE src, VALUE scope, NODE *cref, const char
errat = rb_get_backtrace(errinfo); errat = rb_get_backtrace(errinfo);
mesg = rb_attr_get(errinfo, id_mesg); mesg = rb_attr_get(errinfo, id_mesg);
if (!NIL_P(errat) && RB_TYPE_P(errat, T_ARRAY) && if (!NIL_P(errat) && RB_TYPE_P(errat, T_ARRAY) &&
(bt2 = vm_backtrace(th, -2), RARRAY_LEN(bt2) > 0)) { (bt2 = vm_backtrace_str_ary(th, -2, 0), RARRAY_LEN(bt2) > 0)) {
if (!NIL_P(mesg) && RB_TYPE_P(mesg, T_STRING) && !RSTRING_LEN(mesg)) { if (!NIL_P(mesg) && RB_TYPE_P(mesg, T_STRING) && !RSTRING_LEN(mesg)) {
if (OBJ_FROZEN(mesg)) { if (OBJ_FROZEN(mesg)) {
VALUE m = rb_str_cat(rb_str_dup(RARRAY_PTR(errat)[0]), ": ", 2); VALUE m = rb_str_cat(rb_str_dup(RARRAY_PTR(errat)[0]), ": ", 2);
@ -1620,7 +1622,7 @@ rb_f_caller(int argc, VALUE *argv)
if (lev < 0) if (lev < 0)
rb_raise(rb_eArgError, "negative level (%d)", lev); rb_raise(rb_eArgError, "negative level (%d)", lev);
return vm_backtrace(GET_THREAD(), lev); return vm_backtrace_str_ary(GET_THREAD(), lev, 0);
} }
static int static int
@ -1642,13 +1644,19 @@ print_backtrace(void *arg, VALUE file, int line, VALUE method)
void void
rb_backtrace(void) rb_backtrace(void)
{ {
vm_backtrace_each(GET_THREAD(), -1, NULL, print_backtrace, stderr); vm_backtrace_each(GET_THREAD(), -1, 0, NULL, print_backtrace, stderr);
} }
VALUE VALUE
rb_make_backtrace(void) rb_make_backtrace(void)
{ {
return vm_backtrace(GET_THREAD(), -1); return vm_backtrace_str_ary(GET_THREAD(), -1, 0);
}
VALUE
rb_vm_backtrace_object()
{
return backtrace_object(GET_THREAD(), -1, 0);
} }
VALUE VALUE
@ -1667,13 +1675,13 @@ rb_thread_backtrace(VALUE thval)
return Qnil; return Qnil;
} }
return vm_backtrace(th, 0); return vm_backtrace_str_ary(th, 0, 0);
} }
int int
rb_backtrace_each(rb_backtrace_iter_func *iter, void *arg) rb_backtrace_each(rb_backtrace_iter_func *iter, void *arg)
{ {
return vm_backtrace_each(GET_THREAD(), -1, NULL, iter, arg); return vm_backtrace_each(GET_THREAD(), -1, 0, NULL, iter, arg);
} }
/* /*

View file

@ -1402,3 +1402,18 @@ Init_eval_method(void)
} }
} }
int
rb_method_defined_by(VALUE obj, ID mid, VALUE (*cfunc)(ANYARGS))
{
rb_method_entry_t *me = search_method(CLASS_OF(obj), mid);
if (me && me->def &&
me->def->type == VM_METHOD_TYPE_CFUNC &&
me->def->body.cfunc.func == cfunc) {
return 1;
}
else {
return 0;
}
}