summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorktsj <ktsj@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2015-01-16 02:54:22 +0000
committerktsj <ktsj@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2015-01-16 02:54:22 +0000
commite62a99b924a9b629eca48bfa3f74d1965bc0ecad (patch)
treed3627ccb92d1795c232ddf0dfa8787173571a1ca
parent10fe26fe29681e1b33ee9881efab8e21a7b4fc4d (diff)
* eval_intern.h, vm.c, vm_eval.c, vm_insnhelper.c:
change throw mechanism (not save target ep, but save target cfp). It fixes `unexpected break' bug that occurs when TracePoint#binding is called. [ruby-dev:48797] [Bug #10689] * test/ruby/test_settracefunc.rb: add a test. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@49266 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog10
-rw-r--r--eval_intern.h2
-rw-r--r--test/ruby/test_settracefunc.rb21
-rw-r--r--vm.c69
-rw-r--r--vm_eval.c10
-rw-r--r--vm_insnhelper.c281
6 files changed, 208 insertions, 185 deletions
diff --git a/ChangeLog b/ChangeLog
index c9a1855..b6a96ec 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+Fri Jan 16 11:44:44 2015 Kazuki Tsujimoto <kazuki@callcc.net>
+
+ * eval_intern.h, vm.c, vm_eval.c, vm_insnhelper.c:
+ change throw mechanism (not save target ep, but save target cfp).
+ It fixes `unexpected break' bug that occurs when
+ TracePoint#binding is called.
+ [ruby-dev:48797] [Bug #10689]
+
+ * test/ruby/test_settracefunc.rb: add a test.
+
Thu Jan 15 23:55:15 2015 Tanaka Akira <akr@fsij.org>
* io.c (rb_io_close_m): Don't raise when the IO object is closed.
diff --git a/eval_intern.h b/eval_intern.h
index c457deb..163eeb1 100644
--- a/eval_intern.h
+++ b/eval_intern.h
@@ -207,7 +207,7 @@ enum ruby_tag_type {
(RNODE((obj))->u3.value = (val))
#define GET_THROWOBJ_VAL(obj) ((VALUE)RNODE((obj))->u1.value)
-#define GET_THROWOBJ_CATCH_POINT(obj) ((VALUE*)RNODE((obj))->u2.value)
+#define GET_THROWOBJ_CATCH_POINT(obj) ((rb_control_frame_t*)RNODE((obj))->u2.value)
#define GET_THROWOBJ_STATE(obj) ((int)RNODE((obj))->u3.value)
#define SCOPE_TEST(f) (rb_vm_cref()->nd_visi & (f))
diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb
index 637078a..58e927c 100644
--- a/test/ruby/test_settracefunc.rb
+++ b/test/ruby/test_settracefunc.rb
@@ -977,6 +977,27 @@ class TestSetTraceFunc < Test::Unit::TestCase
end
end
+ def test_trace_point_binding_after_break
+ bug10689 = '[ruby-dev:48797]'
+ assert_in_out_err([], <<-INPUT, [], [], bug10689)
+ class Bug
+ include Enumerable
+
+ def each
+ [0].each do
+ yield
+ end
+ end
+ end
+
+ TracePoint.trace(:c_return) do |tp|
+ tp.binding
+ end
+
+ Bug.new.all? { false }
+ INPUT
+ end
+
def test_tracepoint_b_return_with_next
n = 0
TracePoint.new(:b_return){
diff --git a/vm.c b/vm.c
index 750c756..e6ef3a8 100644
--- a/vm.c
+++ b/vm.c
@@ -28,6 +28,26 @@ VM_EP_LEP(VALUE *ep)
return ep;
}
+static inline rb_control_frame_t *
+rb_vm_search_cf_from_ep(const rb_thread_t * const th, rb_control_frame_t *cfp, const VALUE * const ep)
+{
+ if (!ep) {
+ return NULL;
+ }
+ else {
+ const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(th); /* end of control frame pointer */
+
+ while (cfp < eocfp) {
+ if (cfp->ep == ep) {
+ return cfp;
+ }
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
+ }
+
+ rb_bug("rb_vm_search_cf_from_ep: no corresponding cfp");
+ }
+}
+
VALUE *
rb_vm_ep_local_ep(VALUE *ep)
{
@@ -550,7 +570,6 @@ rb_vm_env_local_variables(VALUE envval)
return local_var_list_finish(&vars);
}
-static void vm_rewrite_ep_in_errinfo(rb_thread_t *th);
static VALUE vm_make_proc_from_block(rb_thread_t *th, rb_block_t *block);
static VALUE vm_make_env_object(rb_thread_t * th, rb_control_frame_t *cfp, VALUE *blockprocptr);
@@ -577,7 +596,6 @@ vm_make_env_object(rb_thread_t *th, rb_control_frame_t *cfp, VALUE *blockprocptr
}
envval = vm_make_env_each(th, cfp, cfp->ep, lep);
- vm_rewrite_ep_in_errinfo(th);
if (PROCDEBUG) {
check_env_value(envval);
@@ -586,32 +604,6 @@ vm_make_env_object(rb_thread_t *th, rb_control_frame_t *cfp, VALUE *blockprocptr
return envval;
}
-static void
-vm_rewrite_ep_in_errinfo(rb_thread_t *th)
-{
- rb_control_frame_t *cfp = th->cfp;
- while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp)) {
- /* rewrite ep in errinfo to point to heap */
- if (RUBY_VM_NORMAL_ISEQ_P(cfp->iseq) &&
- (cfp->iseq->type == ISEQ_TYPE_RESCUE ||
- cfp->iseq->type == ISEQ_TYPE_ENSURE)) {
- VALUE errinfo = cfp->ep[-2]; /* #$! */
- if (RB_TYPE_P(errinfo, T_NODE)) {
- VALUE *escape_ep = GET_THROWOBJ_CATCH_POINT(errinfo);
- if (! ENV_IN_HEAP_P(th, escape_ep)) {
- VALUE epval = *escape_ep;
- if (!SPECIAL_CONST_P(epval) && RBASIC(epval)->klass == rb_cEnv) {
- rb_env_t *epenv;
- GetEnvPtr(epval, epenv);
- SET_THROWOBJ_CATCH_POINT(errinfo, (VALUE)(epenv->env + epenv->local_size));
- }
- }
- }
- }
- cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
- }
-}
-
void
rb_vm_stack_to_heap(rb_thread_t *th)
{
@@ -1152,9 +1144,10 @@ vm_iter_break(rb_thread_t *th, VALUE val)
{
rb_control_frame_t *cfp = th->cfp;
VALUE *ep = VM_CF_PREV_EP(cfp);
+ rb_control_frame_t *target_cfp = rb_vm_search_cf_from_ep(th, cfp, ep);
th->state = TAG_BREAK;
- th->errinfo = (VALUE)NEW_THROW_OBJECT(val, (VALUE)ep, TAG_BREAK);
+ th->errinfo = (VALUE)NEW_THROW_OBJECT(val, (VALUE)target_cfp, TAG_BREAK);
TH_JUMP_TAG(th, TAG_BREAK);
}
@@ -1419,7 +1412,7 @@ vm_exec(rb_thread_t *th)
VALUE catch_iseqval;
rb_control_frame_t *cfp;
VALUE type;
- VALUE *escape_ep;
+ rb_control_frame_t *escape_cfp;
err = th->errinfo;
@@ -1438,14 +1431,14 @@ vm_exec(rb_thread_t *th)
cfp = th->cfp;
epc = cfp->pc - cfp->iseq->iseq_encoded;
- escape_ep = NULL;
+ escape_cfp = NULL;
if (state == TAG_BREAK || state == TAG_RETURN) {
- escape_ep = GET_THROWOBJ_CATCH_POINT(err);
+ escape_cfp = GET_THROWOBJ_CATCH_POINT(err);
- if (cfp->ep == escape_ep) {
+ if (cfp == escape_cfp) {
if (state == TAG_RETURN) {
if (!VM_FRAME_TYPE_FINISH_P(cfp)) {
- SET_THROWOBJ_CATCH_POINT(err, (VALUE)(cfp + 1)->ep);
+ SET_THROWOBJ_CATCH_POINT(err, (VALUE)(cfp + 1));
SET_THROWOBJ_STATE(err, state = TAG_BREAK);
}
else {
@@ -1519,9 +1512,9 @@ vm_exec(rb_thread_t *th)
break;
}
else if (entry->type == CATCH_TYPE_RETRY) {
- VALUE *escape_ep;
- escape_ep = GET_THROWOBJ_CATCH_POINT(err);
- if (cfp->ep == escape_ep) {
+ rb_control_frame_t *escape_cfp;
+ escape_cfp = GET_THROWOBJ_CATCH_POINT(err);
+ if (cfp == escape_cfp) {
cfp->pc = cfp->iseq->iseq_encoded + entry->cont;
th->errinfo = Qnil;
goto vm_loop_start;
@@ -1530,7 +1523,7 @@ vm_exec(rb_thread_t *th)
}
}
}
- else if (state == TAG_BREAK && ((VALUE)escape_ep & ~0x03) == 0) {
+ else if (state == TAG_BREAK && !escape_cfp) {
type = CATCH_TYPE_BREAK;
search_restart_point:
diff --git a/vm_eval.c b/vm_eval.c
index 49ca9d4..a58eefe 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -1100,10 +1100,9 @@ rb_iterate(VALUE (* it_proc) (VALUE), VALUE data1,
else {
VALUE err = th->errinfo;
if (state == TAG_BREAK) {
- VALUE *escape_ep = GET_THROWOBJ_CATCH_POINT(err);
- VALUE *cep = cfp->ep;
+ rb_control_frame_t *escape_cfp = GET_THROWOBJ_CATCH_POINT(err);
- if (cep == escape_ep) {
+ if (cfp == escape_cfp) {
state = 0;
th->state = 0;
th->errinfo = Qnil;
@@ -1116,10 +1115,9 @@ rb_iterate(VALUE (* it_proc) (VALUE), VALUE data1,
}
}
else if (state == TAG_RETRY) {
- VALUE *escape_ep = GET_THROWOBJ_CATCH_POINT(err);
- VALUE *cep = cfp->ep;
+ rb_control_frame_t *escape_cfp = GET_THROWOBJ_CATCH_POINT(err);
- if (cep == escape_ep) {
+ if (cfp == escape_cfp) {
rb_vm_rewind_cfp(th, cfp);
state = 0;
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index b875e2c..f8939b6 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -573,174 +573,175 @@ vm_setinstancevariable(VALUE obj, ID id, VALUE val, IC ic)
}
static VALUE
-vm_throw(rb_thread_t *th, rb_control_frame_t *reg_cfp,
- rb_num_t throw_state, VALUE throwobj)
+vm_throw_continue(rb_thread_t *th, VALUE throwobj)
{
- int state = (int)(throw_state & 0xff);
- int flag = (int)(throw_state & 0x8000);
- rb_num_t level = throw_state >> 16;
+ /* continue throw */
+ VALUE err = throwobj;
- if (state != 0) {
- VALUE *pt = 0;
- if (flag != 0) {
- pt = (void *) 1;
+ if (FIXNUM_P(err)) {
+ th->state = FIX2INT(err);
+ }
+ else if (SYMBOL_P(err)) {
+ th->state = TAG_THROW;
+ }
+ else if (BUILTIN_TYPE(err) == T_NODE) {
+ th->state = GET_THROWOBJ_STATE(err);
+ }
+ else {
+ th->state = TAG_RAISE;
+ /*th->state = FIX2INT(rb_ivar_get(err, idThrowState));*/
+ }
+ return err;
+}
+
+static VALUE
+vm_throw_start(rb_thread_t * const th, rb_control_frame_t * const reg_cfp, int state, const int flag, const rb_num_t level, const VALUE throwobj)
+{
+ rb_control_frame_t *escape_cfp = NULL;
+ const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(th); /* end of control frame pointer */
+
+ if (flag != 0) {
+ /* do nothing */
+ }
+ else if (state == TAG_BREAK) {
+ int is_orphan = 1;
+ VALUE *ep = GET_EP();
+ rb_iseq_t *base_iseq = GET_ISEQ();
+ escape_cfp = reg_cfp;
+
+ search_parent:
+ if (base_iseq->type != ISEQ_TYPE_BLOCK) {
+ if (escape_cfp->iseq->type == ISEQ_TYPE_CLASS) {
+ escape_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(escape_cfp);
+ ep = escape_cfp->ep;
+ base_iseq = escape_cfp->iseq;
+ goto search_parent;
+ }
+ else {
+ ep = VM_EP_PREV_EP(ep);
+ base_iseq = base_iseq->parent_iseq;
+ escape_cfp = rb_vm_search_cf_from_ep(th, escape_cfp, ep);
+ assert(escape_cfp->iseq == base_iseq);
+ }
+ }
+
+ if (VM_FRAME_TYPE(escape_cfp) == VM_FRAME_MAGIC_LAMBDA) {
+ /* lambda{... break ...} */
+ is_orphan = 0;
+ state = TAG_RETURN;
}
else {
- if (state == TAG_BREAK) {
- rb_control_frame_t *cfp = GET_CFP();
- VALUE *ep = GET_EP();
- int is_orphan = 1;
- rb_iseq_t *base_iseq = GET_ISEQ();
-
- search_parent:
- if (cfp->iseq->type != ISEQ_TYPE_BLOCK) {
- if (cfp->iseq->type == ISEQ_TYPE_CLASS) {
- cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
- ep = cfp->ep;
- goto search_parent;
- }
- ep = VM_EP_PREV_EP(ep);
- base_iseq = base_iseq->parent_iseq;
+ ep = VM_EP_PREV_EP(ep);
+
+ while (escape_cfp < eocfp) {
+ if (escape_cfp->ep == ep) {
+ const VALUE epc = escape_cfp->pc - escape_cfp->iseq->iseq_encoded;
+ const rb_iseq_t * const iseq = escape_cfp->iseq;
+ const struct iseq_catch_table * const ct = iseq->catch_table;
+ const int ct_size = ct->size;
+ int i;
- while ((VALUE *) cfp < th->stack + th->stack_size) {
- if (cfp->ep == ep) {
- goto search_parent;
- }
- cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
- }
- rb_bug("VM (throw): can't find break base.");
- }
+ for (i=0; i<ct_size; i++) {
+ const struct iseq_catch_table_entry * const entry = &ct->entries[i];;
- if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_LAMBDA) {
- /* lambda{... break ...} */
- is_orphan = 0;
- pt = cfp->ep;
- state = TAG_RETURN;
- }
- else {
- ep = VM_EP_PREV_EP(ep);
-
- while ((VALUE *)cfp < th->stack + th->stack_size) {
- if (cfp->ep == ep) {
- VALUE epc = cfp->pc - cfp->iseq->iseq_encoded;
- rb_iseq_t *iseq = cfp->iseq;
- struct iseq_catch_table *ct = iseq->catch_table;
- struct iseq_catch_table_entry *entry;
- int i;
-
- for (i=0; i<ct->size; i++) {
- entry = &ct->entries[i];
-
- if (entry->type == CATCH_TYPE_BREAK &&
- entry->start < epc && entry->end >= epc) {
- if (entry->cont == epc) {
- goto found;
- }
- else {
- break;
- }
- }
+ if (entry->type == CATCH_TYPE_BREAK && entry->start < epc && entry->end >= epc) {
+ if (entry->cont == epc) { /* found! */
+ is_orphan = 0;
}
break;
-
- found:
- pt = ep;
- is_orphan = 0;
- break;
}
- cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
}
+ break;
}
- if (is_orphan) {
- rb_vm_localjump_error("break from proc-closure", throwobj, TAG_BREAK);
- }
- }
- else if (state == TAG_RETRY) {
- rb_num_t i;
- pt = VM_EP_PREV_EP(GET_EP());
- for (i = 0; i < level; i++) {
- pt = GC_GUARDED_PTR_REF((VALUE *) * pt);
- }
+ escape_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(escape_cfp);
}
- else if (state == TAG_RETURN) {
- rb_control_frame_t *cfp = GET_CFP();
- VALUE *ep = GET_EP();
- VALUE *target_lep = VM_CF_LEP(cfp);
- int in_class_frame = 0;
-
- /* check orphan and get dfp */
- while ((VALUE *) cfp < th->stack + th->stack_size) {
- VALUE *lep = VM_CF_LEP(cfp);
-
- if (!target_lep) {
- target_lep = lep;
- }
+ }
- if (lep == target_lep && cfp->iseq->type == ISEQ_TYPE_CLASS) {
- in_class_frame = 1;
- target_lep = 0;
- }
+ if (is_orphan) {
+ rb_vm_localjump_error("break from proc-closure", throwobj, TAG_BREAK);
+ }
+ }
+ else if (state == TAG_RETRY) {
+ rb_num_t i;
+ VALUE *ep = VM_EP_PREV_EP(GET_EP());
- if (lep == target_lep) {
- if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_LAMBDA) {
- VALUE *tep = ep;
+ for (i = 0; i < level; i++) {
+ ep = VM_EP_PREV_EP(ep);
+ }
- if (in_class_frame) {
- /* lambda {class A; ... return ...; end} */
- ep = cfp->ep;
- goto valid_return;
- }
+ escape_cfp = rb_vm_search_cf_from_ep(th, reg_cfp, ep);
+ }
+ else if (state == TAG_RETURN) {
+ VALUE *current_ep = GET_EP();
+ VALUE *target_lep = VM_EP_LEP(current_ep);
+ int in_class_frame = 0;
+ escape_cfp = reg_cfp;
- while (target_lep != tep) {
- if (cfp->ep == tep) {
- /* in lambda */
- ep = cfp->ep;
- goto valid_return;
- }
- tep = VM_EP_PREV_EP(tep);
- }
- }
- }
+ while (escape_cfp < eocfp) {
+ VALUE *lep = VM_CF_LEP(escape_cfp);
+
+ if (!target_lep) {
+ target_lep = lep;
+ }
+
+ if (lep == target_lep && escape_cfp->iseq->type == ISEQ_TYPE_CLASS) {
+ in_class_frame = 1;
+ target_lep = 0;
+ }
- if (cfp->ep == target_lep && cfp->iseq->type == ISEQ_TYPE_METHOD) {
- ep = target_lep;
+ if (lep == target_lep) {
+ if (VM_FRAME_TYPE(escape_cfp) == VM_FRAME_MAGIC_LAMBDA) {
+ if (in_class_frame) {
+ /* lambda {class A; ... return ...; end} */
goto valid_return;
}
+ else {
+ VALUE *tep = current_ep;
- cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
+ while (target_lep != tep) {
+ if (escape_cfp->ep == tep) {
+ /* in lambda */
+ goto valid_return;
+ }
+ tep = VM_EP_PREV_EP(tep);
+ }
+ }
}
-
- rb_vm_localjump_error("unexpected return", throwobj, TAG_RETURN);
-
- valid_return:
- pt = ep;
}
- else {
- rb_bug("isns(throw): unsupport throw type");
+
+ if (escape_cfp->ep == target_lep && escape_cfp->iseq->type == ISEQ_TYPE_METHOD) {
+ goto valid_return;
}
+
+ escape_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(escape_cfp);
}
- th->state = state;
- return (VALUE)NEW_THROW_OBJECT(throwobj, (VALUE) pt, state);
+ rb_vm_localjump_error("unexpected return", throwobj, TAG_RETURN);
+
+ valid_return:;
+ /* do nothing */
}
else {
- /* continue throw */
- VALUE err = throwobj;
+ rb_bug("isns(throw): unsupport throw type");
+ }
- if (FIXNUM_P(err)) {
- th->state = FIX2INT(err);
- }
- else if (SYMBOL_P(err)) {
- th->state = TAG_THROW;
- }
- else if (BUILTIN_TYPE(err) == T_NODE) {
- th->state = GET_THROWOBJ_STATE(err);
- }
- else {
- th->state = TAG_RAISE;
- /*th->state = FIX2INT(rb_ivar_get(err, idThrowState));*/
- }
- return err;
+ th->state = state;
+ return (VALUE)NEW_THROW_OBJECT(throwobj, (VALUE)escape_cfp, state);
+}
+
+static VALUE
+vm_throw(rb_thread_t *th, rb_control_frame_t *reg_cfp,
+ rb_num_t throw_state, VALUE throwobj)
+{
+ const int state = (int)(throw_state & 0xff);
+ const int flag = (int)(throw_state & 0x8000);
+ const rb_num_t level = throw_state >> 16;
+
+ if (state != 0) {
+ return vm_throw_start(th, reg_cfp, state, flag, level, throwobj);
+ }
+ else {
+ return vm_throw_continue(th, throwobj);
}
}