check break target correctly.

* compile.c (iseq_compile_each0): save target child_iseq in the catch-table
  for break. This iseq is not for continuation, but for search key at
  vm_throw_start().

* vm_insnhelper.c (vm_throw_start): check saved iseq first.

* iseq.h: add comment for it.

* test/ruby/test_iterator.rb (test_ljump): add a test for the issue:
    def call b; b.call; end
    call(Proc.new{break}){} #=> (1) should raise LocalJumpError
    call(Proc.new{break})   #=> (2) shoudd raies LocalJumpError, too.
  but (1) doesn't raise LocalJumpError.

  This issue is reported by Matz.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59043 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
ko1 2017-06-08 04:13:51 +00:00
parent 0318de23d4
commit 7d8a415bc2
4 changed files with 28 additions and 5 deletions

View File

@ -4399,17 +4399,20 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popp
const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
LABEL *retry_label = NEW_LABEL(line);
LABEL *retry_end_l = NEW_LABEL(line);
const rb_iseq_t *child_iseq;
ADD_LABEL(ret, retry_label);
if (nd_type(node) == NODE_FOR) {
CHECK(COMPILE(ret, "iter caller (for)", node->nd_iter));
ISEQ_COMPILE_DATA(iseq)->current_block = NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
ISEQ_TYPE_BLOCK, line);
ADD_SEND_WITH_BLOCK(ret, line, idEach, INT2FIX(0), ISEQ_COMPILE_DATA(iseq)->current_block);
ADD_SEND_WITH_BLOCK(ret, line, idEach, INT2FIX(0), child_iseq);
}
else {
ISEQ_COMPILE_DATA(iseq)->current_block = NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
ISEQ_TYPE_BLOCK, line);
CHECK(COMPILE(ret, "iter caller", node->nd_iter));
}
@ -4421,7 +4424,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, NODE *node, int popp
ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, NULL, retry_end_l);
ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, child_iseq, retry_end_l);
break;
}

14
iseq.h
View File

@ -151,7 +151,21 @@ struct iseq_catch_table_entry {
CATCH_TYPE_REDO = INT2FIX(5),
CATCH_TYPE_NEXT = INT2FIX(6)
} type;
/*
* iseq type:
* CATCH_TYPE_RESCUE, CATCH_TYPE_ENSURE:
* use iseq as continuation.
*
* CATCH_TYPE_BREAK (iter):
* use iseq as key.
*
* CATCH_TYPE_BREAK (while), CATCH_TYPE_RETRY,
* CATCH_TYPE_REDO, CATCH_TYPE_NEXT:
* NULL.
*/
const rb_iseq_t *iseq;
unsigned int start;
unsigned int end;
unsigned int cont;

View File

@ -279,6 +279,9 @@ class TestIterator < Test::Unit::TestCase
def proc_call(&b)
b.call
end
def proc_call2(b)
b.call
end
def proc_yield()
yield
end
@ -300,6 +303,7 @@ class TestIterator < Test::Unit::TestCase
def test_ljump
assert_raise(LocalJumpError) {get_block{break}.call}
assert_raise(LocalJumpError) {proc_call2(get_block{break}){}}
# cannot use assert_nothing_raised due to passing block.
begin

View File

@ -1090,7 +1090,9 @@ vm_throw_start(rb_thread_t *const th, rb_control_frame_t *const reg_cfp, enum ru
for (i=0; i<ct_size; i++) {
const struct iseq_catch_table_entry * const entry = &ct->entries[i];
if (entry->type == CATCH_TYPE_BREAK && entry->start < epc && entry->end >= epc) {
if (entry->type == CATCH_TYPE_BREAK &&
entry->iseq == base_iseq &&
entry->start < epc && entry->end >= epc) {
if (entry->cont == epc) { /* found! */
is_orphan = 0;
}