diff options
author | ktsj <ktsj@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2015-01-16 02:54:22 +0000 |
---|---|---|
committer | ktsj <ktsj@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2015-01-16 02:54:22 +0000 |
commit | e62a99b924a9b629eca48bfa3f74d1965bc0ecad (patch) | |
tree | d3627ccb92d1795c232ddf0dfa8787173571a1ca /vm_insnhelper.c | |
parent | 10fe26fe29681e1b33ee9881efab8e21a7b4fc4d (diff) | |
download | ruby-e62a99b924a9b629eca48bfa3f74d1965bc0ecad.tar.gz |
* 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
Diffstat (limited to 'vm_insnhelper.c')
-rw-r--r-- | vm_insnhelper.c | 281 |
1 files changed, 141 insertions, 140 deletions
diff --git a/vm_insnhelper.c b/vm_insnhelper.c index b875e2c627..f8939b6bf1 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); } } |