fix raise in exception with jump
add_ensure_iseq() adds ensure block to the end of jump such as next/redo/return. However, if the rescue cause are in the body, this rescue catches the exception in ensure clause. iter do next rescue R ensure raise end In this case, R should not be executed, but executed without this patch. Fixes [Bug #13930] Fixes [Bug #16618] A part of tests are written by @jeremyevans https://github.com/ruby/ruby/pull/4291
This commit is contained in:
parent
5512353d97
commit
609de71f04
Notes:
git
2021-04-22 11:34:00 +09:00
34
compile.c
34
compile.c
@ -5356,9 +5356,22 @@ add_ensure_range(rb_iseq_t *iseq, struct ensure_range *erange,
|
||||
erange->next = ne;
|
||||
}
|
||||
|
||||
static bool
|
||||
can_add_ensure_iseq(const rb_iseq_t *iseq)
|
||||
{
|
||||
if (ISEQ_COMPILE_DATA(iseq)->in_rescue && ISEQ_COMPILE_DATA(iseq)->ensure_node_stack) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return)
|
||||
{
|
||||
assert(can_add_ensure_iseq(iseq));
|
||||
|
||||
struct iseq_compile_data_ensure_node_stack *enlp =
|
||||
ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
|
||||
struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
|
||||
@ -6850,7 +6863,7 @@ compile_break(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
|
||||
const int line = nd_line(node);
|
||||
unsigned long throw_flag = 0;
|
||||
|
||||
if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0) {
|
||||
if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
|
||||
/* while/until */
|
||||
LABEL *splabel = NEW_LABEL(0);
|
||||
ADD_LABEL(ret, splabel);
|
||||
@ -6909,7 +6922,7 @@ compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
|
||||
const int line = nd_line(node);
|
||||
unsigned long throw_flag = 0;
|
||||
|
||||
if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0) {
|
||||
if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
|
||||
LABEL *splabel = NEW_LABEL(0);
|
||||
debugs("next in while loop\n");
|
||||
ADD_LABEL(ret, splabel);
|
||||
@ -6922,7 +6935,7 @@ compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
|
||||
ADD_INSN(ret, line, putnil);
|
||||
}
|
||||
}
|
||||
else if (ISEQ_COMPILE_DATA(iseq)->end_label) {
|
||||
else if (ISEQ_COMPILE_DATA(iseq)->end_label && can_add_ensure_iseq(iseq)) {
|
||||
LABEL *splabel = NEW_LABEL(0);
|
||||
debugs("next in block\n");
|
||||
ADD_LABEL(ret, splabel);
|
||||
@ -6982,7 +6995,7 @@ compile_redo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
|
||||
{
|
||||
const int line = nd_line(node);
|
||||
|
||||
if (ISEQ_COMPILE_DATA(iseq)->redo_label) {
|
||||
if (ISEQ_COMPILE_DATA(iseq)->redo_label && can_add_ensure_iseq(iseq)) {
|
||||
LABEL *splabel = NEW_LABEL(0);
|
||||
debugs("redo in while");
|
||||
ADD_LABEL(ret, splabel);
|
||||
@ -6994,7 +7007,7 @@ compile_redo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
|
||||
ADD_INSN(ret, line, putnil);
|
||||
}
|
||||
}
|
||||
else if (iseq->body->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label) {
|
||||
else if (iseq->body->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label && can_add_ensure_iseq(iseq)) {
|
||||
LABEL *splabel = NEW_LABEL(0);
|
||||
|
||||
debugs("redo in block");
|
||||
@ -7080,7 +7093,14 @@ compile_rescue(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
|
||||
lstart->rescued = LABEL_RESCUE_BEG;
|
||||
lend->rescued = LABEL_RESCUE_END;
|
||||
ADD_LABEL(ret, lstart);
|
||||
CHECK(COMPILE(ret, "rescue head", node->nd_head));
|
||||
|
||||
bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
|
||||
ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
|
||||
{
|
||||
CHECK(COMPILE(ret, "rescue head", node->nd_head));
|
||||
}
|
||||
ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
|
||||
|
||||
ADD_LABEL(ret, lend);
|
||||
if (node->nd_else) {
|
||||
ADD_INSN(ret, line, pop);
|
||||
@ -7241,7 +7261,7 @@ compile_return(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
|
||||
|
||||
CHECK(COMPILE(ret, "return nd_stts (return val)", retval));
|
||||
|
||||
if (type == ISEQ_TYPE_METHOD) {
|
||||
if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
|
||||
add_ensure_iseq(ret, iseq, 1);
|
||||
ADD_TRACE(ret, RUBY_EVENT_RETURN);
|
||||
ADD_INSN(ret, line, leave);
|
||||
|
1
iseq.h
1
iseq.h
@ -101,6 +101,7 @@ struct iseq_compile_data {
|
||||
struct iseq_compile_data_storage *storage_head;
|
||||
struct iseq_compile_data_storage *storage_current;
|
||||
} insn;
|
||||
bool in_rescue;
|
||||
int loopval_popped; /* used by NODE_BREAK */
|
||||
int last_line;
|
||||
int label_no;
|
||||
|
@ -78,6 +78,66 @@ class TestException < Test::Unit::TestCase
|
||||
assert(!bad)
|
||||
end
|
||||
|
||||
def test_exception_in_ensure_with_next
|
||||
string = "[ruby-core:82936] [Bug #13930]"
|
||||
assert_raise_with_message(RuntimeError, string) do
|
||||
lambda do
|
||||
next
|
||||
rescue
|
||||
assert(false)
|
||||
ensure
|
||||
raise string
|
||||
end.call
|
||||
assert(false)
|
||||
end
|
||||
|
||||
assert_raise_with_message(RuntimeError, string) do
|
||||
flag = true
|
||||
while flag
|
||||
flag = false
|
||||
begin
|
||||
next
|
||||
rescue
|
||||
assert(false)
|
||||
ensure
|
||||
raise string
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_exception_in_ensure_with_redo
|
||||
string = "[ruby-core:82936] [Bug #13930]"
|
||||
|
||||
assert_raise_with_message(RuntimeError, string) do
|
||||
i = 0
|
||||
lambda do
|
||||
i += 1
|
||||
redo if i < 2
|
||||
rescue
|
||||
assert(false)
|
||||
ensure
|
||||
raise string
|
||||
end.call
|
||||
assert(false)
|
||||
end
|
||||
end
|
||||
|
||||
def test_exception_in_ensure_with_return
|
||||
@string = "[ruby-core:97104] [Bug #16618]"
|
||||
def self.meow
|
||||
return
|
||||
assert(false)
|
||||
rescue
|
||||
assert(false)
|
||||
ensure
|
||||
raise @string
|
||||
end
|
||||
assert_raise_with_message(RuntimeError, @string) do
|
||||
meow
|
||||
end
|
||||
end
|
||||
|
||||
def test_errinfo_in_debug
|
||||
bug9568 = EnvUtil.labeled_class("[ruby-core:61091] [Bug #9568]", RuntimeError) do
|
||||
def to_s
|
||||
|
Loading…
x
Reference in New Issue
Block a user