summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog9
-rw-r--r--eval.c25
-rw-r--r--test/ruby/test_settracefunc.rb25
-rw-r--r--vm.c43
-rw-r--r--vm_eval.c15
-rw-r--r--vm_insnhelper.c42
6 files changed, 120 insertions, 39 deletions
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 <ko1@atdot.net>
+
+ * 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@fsij.org>
* 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();
- }
- 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 @@ 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:{