ruby-changes:14556
From: ko1 <ko1@a...>
Date: Sun, 24 Jan 2010 22:52:50 +0900 (JST)
Subject: [ruby-changes:14556] Ruby:r26395 (trunk): * eval.c, vm.c, vm_eval.c, vm_insnhelper.c: fix issues about
ko1 2010-01-24 22:52:32 +0900 (Sun, 24 Jan 2010) New Revision: 26395 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=26395 Log: * 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. Modified files: trunk/ChangeLog trunk/eval.c trunk/test/ruby/test_settracefunc.rb trunk/vm.c trunk/vm_eval.c trunk/vm_insnhelper.c Index: ChangeLog =================================================================== --- ChangeLog (revision 26394) +++ ChangeLog (revision 26395) @@ -1,3 +1,12 @@ +Sun Jan 24 22:48:05 2010 Koichi Sasada <ko1@a...> + + * 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 <akr@f...> * string.c (rb_enc_strlen_cr): increment by rb_enc_mbminlen(enc) for Index: vm_eval.c =================================================================== --- vm_eval.c (revision 26394) +++ vm_eval.c (revision 26395) @@ -814,6 +814,9 @@ 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 @@ 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); */ Index: eval.c =================================================================== --- eval.c (revision 26394) +++ eval.c (revision 26395) @@ -352,11 +352,10 @@ 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_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 @@ 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 Index: vm.c =================================================================== --- vm.c (revision 26394) +++ vm.c (revision 26395) @@ -1001,6 +1001,27 @@ #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 @@ 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 @@ 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 { Index: vm_insnhelper.c =================================================================== --- vm_insnhelper.c (revision 26394) +++ vm_insnhelper.c (revision 26395) @@ -360,44 +360,30 @@ 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(); - } - 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 = 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; - 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); - 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"); + } - if (reg_cfp != th->cfp + 1) { - rb_bug("cfp consistency error - send"); - } + vm_pop_frame(th); - 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 @@ } 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:{ Index: test/ruby/test_settracefunc.rb =================================================================== --- test/ruby/test_settracefunc.rb (revision 26394) +++ test/ruby/test_settracefunc.rb (revision 26395) @@ -263,8 +263,31 @@ 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 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/