diff --git a/ChangeLog b/ChangeLog index 5e58c5a597..5953d17e4f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Sun Jan 24 22:48:05 2010 Koichi Sasada + + * eval.c, vm.c, vm_eval.c, vm_insnhelper.c: fix issues about + return and c-return trace. This issue skips (c-)return event + with global jump such as break or return. This fix make vm invoke + hooks at stack rewind timing. fix [ruby-core:27606] [Bug #2610]. + + * test/ruby/test_settracefunc.rb: add a test for above. + Sun Jan 24 14:21:48 2010 Tanaka Akira * string.c (rb_enc_strlen_cr): increment by rb_enc_mbminlen(enc) for diff --git a/eval.c b/eval.c index c5a72dc609..f60a7e4a5a 100644 --- a/eval.c +++ b/eval.c @@ -352,11 +352,10 @@ rb_frozen_class_p(VALUE klass) NORETURN(static void rb_longjmp(int, volatile VALUE)); static void -rb_longjmp(int tag, volatile VALUE mesg) +setup_exception(rb_thread_t *th, int tag, volatile VALUE mesg) { VALUE at; VALUE e; - rb_thread_t *th = GET_THREAD(); const char *file; volatile int line = 0; @@ -425,10 +424,15 @@ rb_longjmp(int tag, volatile VALUE mesg) rb_trap_restore_mask(); if (tag != TAG_FATAL) { - EXEC_EVENT_HOOK(th, RUBY_EVENT_RAISE, th->cfp->self, - 0 /* TODO: id */, 0 /* TODO: klass */); + EXEC_EVENT_HOOK(th, RUBY_EVENT_RAISE, th->cfp->self, 0, 0); } +} +static void +rb_longjmp(int tag, volatile VALUE mesg) +{ + rb_thread_t *th = GET_THREAD(); + setup_exception(th, tag, mesg); rb_thread_raised_clear(th); JUMP_TAG(tag); } @@ -559,9 +563,18 @@ void rb_raise_jump(VALUE mesg) { rb_thread_t *th = GET_THREAD(); + rb_control_frame_t *cfp = th->cfp; + VALUE klass = cfp->me->klass; + VALUE self = cfp->self; + ID mid = cfp->me->called_id; + th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp); - /* TODO: fix me */ - rb_longjmp(TAG_RAISE, mesg); + + setup_exception(th, TAG_RAISE, mesg); + + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, self, mid, klass); + rb_thread_raised_clear(th); + JUMP_TAG(TAG_RAISE); } void diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index 7fd7cc6534..f66b728146 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -263,8 +263,31 @@ class TestSetTraceFunc < Test::Unit::TestCase assert_equal([], events) end + def test_break # [ruby-core:27606] [Bug #2610] + events = [] + eval <<-EOF.gsub(/^.*?: /, "") + 1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass| + 2: events << [event, lineno, mid, klass] + 3: }) + 4: [1,2,3].any? {|n| n} + 8: set_trace_func(nil) + EOF + + [["c-return", 3, :set_trace_func, Kernel], + ["line", 4, __method__, self.class], + ["c-call", 4, :any?, Enumerable], + ["c-call", 4, :each, Array], + ["line", 4, __method__, self.class], + ["c-return", 4, :each, Array], + ["c-return", 4, :any?, Enumerable], + ["line", 5, __method__, self.class], + ["c-call", 5, :set_trace_func, Kernel]].each{|e| + assert_equal(e, events.shift) + } + end + def test_invalid_proc - assert_raise(TypeError) { set_trace_func(1) } + assert_raise(TypeError) { set_trace_func(1) } end def test_raise_in_trace diff --git a/vm.c b/vm.c index 1fe25a1ca3..bb19ecb511 100644 --- a/vm.c +++ b/vm.c @@ -1001,6 +1001,27 @@ vm_init_redefined_flag(void) #undef OP } +/* for vm development */ + +static const char * +vm_frametype_name(const rb_control_frame_t *cfp) +{ + switch (VM_FRAME_TYPE(cfp)) { + case VM_FRAME_MAGIC_METHOD: return "method"; + case VM_FRAME_MAGIC_BLOCK: return "block"; + case VM_FRAME_MAGIC_CLASS: return "class"; + case VM_FRAME_MAGIC_TOP: return "top"; + case VM_FRAME_MAGIC_FINISH: return "finish"; + case VM_FRAME_MAGIC_CFUNC: return "cfunc"; + case VM_FRAME_MAGIC_PROC: return "proc"; + case VM_FRAME_MAGIC_IFUNC: return "ifunc"; + case VM_FRAME_MAGIC_EVAL: return "eval"; + case VM_FRAME_MAGIC_LAMBDA: return "lambda"; + default: + rb_bug("unknown frame"); + } +} + /* evaluator body */ /* finish @@ -1137,7 +1158,11 @@ vm_exec(rb_thread_t *th) cont_pc = cont_sp = catch_iseqval = 0; while (th->cfp->pc == 0 || th->cfp->iseq == 0) { - th->cfp++; + if (UNLIKELY(VM_FRAME_TYPE(th->cfp) == VM_FRAME_MAGIC_CFUNC)) { + const rb_method_entry_t *me = th->cfp->me; + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, th->cfp->self, me->called_id, me->klass); + } + th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp); } cfp = th->cfp; @@ -1298,8 +1323,20 @@ vm_exec(rb_thread_t *th) goto vm_loop_start; } else { - th->cfp++; - if (th->cfp->pc != &finish_insn_seq[0]) { + /* skip frame */ + + switch (VM_FRAME_TYPE(th->cfp)) { + case VM_FRAME_MAGIC_METHOD: + EXEC_EVENT_HOOK(th, RUBY_EVENT_RETURN, th->cfp->self, 0, 0); + break; + case VM_FRAME_MAGIC_CLASS: + EXEC_EVENT_HOOK(th, RUBY_EVENT_END, th->cfp->self, 0, 0); + break; + } + + th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp); + + if (VM_FRAME_TYPE(th->cfp) != VM_FRAME_MAGIC_FINISH) { goto exception_handler; } else { diff --git a/vm_eval.c b/vm_eval.c index 2d16ba6653..762453e5fe 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -814,6 +814,9 @@ rb_f_loop(VALUE self) return Qnil; /* dummy */ } +static const char * +vm_frametype_name(const rb_control_frame_t *cfp); + VALUE rb_iterate(VALUE (* it_proc) (VALUE), VALUE data1, VALUE (* bl_proc) (ANYARGS), VALUE data2) @@ -852,7 +855,17 @@ rb_iterate(VALUE (* it_proc) (VALUE), VALUE data1, state = 0; th->state = 0; th->errinfo = Qnil; - th->cfp = cfp; + + /* check skipped frame */ + while (th->cfp != cfp) { + /* printf("skipped frame: %s\n", vm_frametype_name(th->cfp)); */ + if (UNLIKELY(VM_FRAME_TYPE(th->cfp) == VM_FRAME_MAGIC_CFUNC)) { + const rb_method_entry_t *me = th->cfp->me; + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, th->cfp->self, me->called_id, me->klass); + } + + th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp); + } } else{ /* SDR(); printf("%p, %p\n", cdfp, escape_dfp); */ diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 8f992d2a8f..54d54ff195 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -360,44 +360,30 @@ call_cfunc(VALUE (*func)(), VALUE recv, static inline VALUE vm_call_cfunc(rb_thread_t *th, rb_control_frame_t *reg_cfp, - int num, VALUE recv, const rb_block_t *blockptr, VALUE flag, + int num, VALUE recv, const rb_block_t *blockptr, const rb_method_entry_t *me) { VALUE val = 0; int state = 0; const rb_method_definition_t *def = me->def; - VALUE klass = me->klass; - ID id = me->called_id; + rb_control_frame_t *cfp; - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, id, klass); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, recv, me->called_id, me->klass); - TH_PUSH_TAG(th); - /* TODO: fix me. separate event */ - if (th->event_flags & (RUBY_EVENT_C_RETURN | RUBY_EVENT_VM)) { - state = TH_EXEC_TAG(); + cfp = vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, + recv, (VALUE) blockptr, 0, reg_cfp->sp, 0, 1); + cfp->me = me; + reg_cfp->sp -= num + 1; + + val = call_cfunc(def->body.cfunc.func, recv, (int)def->body.cfunc.argc, num, reg_cfp->sp + 1); + + if (reg_cfp != th->cfp + 1) { + rb_bug("cfp consistency error - send"); } - else { - _th->tag = _tag.prev; - } - if (state == 0) { - rb_control_frame_t *cfp = - vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, - recv, (VALUE) blockptr, 0, reg_cfp->sp, 0, 1); - cfp->me = me; - reg_cfp->sp -= num + 1; + vm_pop_frame(th); - val = call_cfunc(def->body.cfunc.func, recv, (int)def->body.cfunc.argc, num, reg_cfp->sp + 1); - - if (reg_cfp != th->cfp + 1) { - rb_bug("cfp consistency error - send"); - } - - vm_pop_frame(th); - } - TH_POP_TAG(); - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, id, klass); - if (state) TH_JUMP_TAG(th, state); + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, recv, me->called_id, me->klass); return val; } @@ -512,7 +498,7 @@ vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, } case VM_METHOD_TYPE_NOTIMPLEMENTED: case VM_METHOD_TYPE_CFUNC:{ - val = vm_call_cfunc(th, cfp, num, recv, blockptr, flag, me); + val = vm_call_cfunc(th, cfp, num, recv, blockptr, me); break; } case VM_METHOD_TYPE_ATTRSET:{