Skip to content

Commit 609de71

Browse files
committed
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 #4291
1 parent 5512353 commit 609de71

File tree

3 files changed

+88
-7
lines changed

3 files changed

+88
-7
lines changed

compile.c

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5356,9 +5356,22 @@ add_ensure_range(rb_iseq_t *iseq, struct ensure_range *erange,
53565356
erange->next = ne;
53575357
}
53585358

5359+
static bool
5360+
can_add_ensure_iseq(const rb_iseq_t *iseq)
5361+
{
5362+
if (ISEQ_COMPILE_DATA(iseq)->in_rescue && ISEQ_COMPILE_DATA(iseq)->ensure_node_stack) {
5363+
return false;
5364+
}
5365+
else {
5366+
return true;
5367+
}
5368+
}
5369+
53595370
static void
53605371
add_ensure_iseq(LINK_ANCHOR *const ret, rb_iseq_t *iseq, int is_return)
53615372
{
5373+
assert(can_add_ensure_iseq(iseq));
5374+
53625375
struct iseq_compile_data_ensure_node_stack *enlp =
53635376
ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
53645377
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
68506863
const int line = nd_line(node);
68516864
unsigned long throw_flag = 0;
68526865

6853-
if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0) {
6866+
if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
68546867
/* while/until */
68556868
LABEL *splabel = NEW_LABEL(0);
68566869
ADD_LABEL(ret, splabel);
@@ -6909,7 +6922,7 @@ compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
69096922
const int line = nd_line(node);
69106923
unsigned long throw_flag = 0;
69116924

6912-
if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0) {
6925+
if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0 && can_add_ensure_iseq(iseq)) {
69136926
LABEL *splabel = NEW_LABEL(0);
69146927
debugs("next in while loop\n");
69156928
ADD_LABEL(ret, splabel);
@@ -6922,7 +6935,7 @@ compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
69226935
ADD_INSN(ret, line, putnil);
69236936
}
69246937
}
6925-
else if (ISEQ_COMPILE_DATA(iseq)->end_label) {
6938+
else if (ISEQ_COMPILE_DATA(iseq)->end_label && can_add_ensure_iseq(iseq)) {
69266939
LABEL *splabel = NEW_LABEL(0);
69276940
debugs("next in block\n");
69286941
ADD_LABEL(ret, splabel);
@@ -6982,7 +6995,7 @@ compile_redo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
69826995
{
69836996
const int line = nd_line(node);
69846997

6985-
if (ISEQ_COMPILE_DATA(iseq)->redo_label) {
6998+
if (ISEQ_COMPILE_DATA(iseq)->redo_label && can_add_ensure_iseq(iseq)) {
69866999
LABEL *splabel = NEW_LABEL(0);
69877000
debugs("redo in while");
69887001
ADD_LABEL(ret, splabel);
@@ -6994,7 +7007,7 @@ compile_redo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, in
69947007
ADD_INSN(ret, line, putnil);
69957008
}
69967009
}
6997-
else if (iseq->body->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label) {
7010+
else if (iseq->body->type != ISEQ_TYPE_EVAL && ISEQ_COMPILE_DATA(iseq)->start_label && can_add_ensure_iseq(iseq)) {
69987011
LABEL *splabel = NEW_LABEL(0);
69997012

70007013
debugs("redo in block");
@@ -7080,7 +7093,14 @@ compile_rescue(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
70807093
lstart->rescued = LABEL_RESCUE_BEG;
70817094
lend->rescued = LABEL_RESCUE_END;
70827095
ADD_LABEL(ret, lstart);
7083-
CHECK(COMPILE(ret, "rescue head", node->nd_head));
7096+
7097+
bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
7098+
ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
7099+
{
7100+
CHECK(COMPILE(ret, "rescue head", node->nd_head));
7101+
}
7102+
ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
7103+
70847104
ADD_LABEL(ret, lend);
70857105
if (node->nd_else) {
70867106
ADD_INSN(ret, line, pop);
@@ -7241,7 +7261,7 @@ compile_return(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node,
72417261

72427262
CHECK(COMPILE(ret, "return nd_stts (return val)", retval));
72437263

7244-
if (type == ISEQ_TYPE_METHOD) {
7264+
if (type == ISEQ_TYPE_METHOD && can_add_ensure_iseq(iseq)) {
72457265
add_ensure_iseq(ret, iseq, 1);
72467266
ADD_TRACE(ret, RUBY_EVENT_RETURN);
72477267
ADD_INSN(ret, line, leave);

iseq.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ struct iseq_compile_data {
101101
struct iseq_compile_data_storage *storage_head;
102102
struct iseq_compile_data_storage *storage_current;
103103
} insn;
104+
bool in_rescue;
104105
int loopval_popped; /* used by NODE_BREAK */
105106
int last_line;
106107
int label_no;

test/ruby/test_exception.rb

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,66 @@ def test_exception_ensure_2 # just duplication?
7878
assert(!bad)
7979
end
8080

81+
def test_exception_in_ensure_with_next
82+
string = "[ruby-core:82936] [Bug #13930]"
83+
assert_raise_with_message(RuntimeError, string) do
84+
lambda do
85+
next
86+
rescue
87+
assert(false)
88+
ensure
89+
raise string
90+
end.call
91+
assert(false)
92+
end
93+
94+
assert_raise_with_message(RuntimeError, string) do
95+
flag = true
96+
while flag
97+
flag = false
98+
begin
99+
next
100+
rescue
101+
assert(false)
102+
ensure
103+
raise string
104+
end
105+
end
106+
end
107+
end
108+
109+
def test_exception_in_ensure_with_redo
110+
string = "[ruby-core:82936] [Bug #13930]"
111+
112+
assert_raise_with_message(RuntimeError, string) do
113+
i = 0
114+
lambda do
115+
i += 1
116+
redo if i < 2
117+
rescue
118+
assert(false)
119+
ensure
120+
raise string
121+
end.call
122+
assert(false)
123+
end
124+
end
125+
126+
def test_exception_in_ensure_with_return
127+
@string = "[ruby-core:97104] [Bug #16618]"
128+
def self.meow
129+
return
130+
assert(false)
131+
rescue
132+
assert(false)
133+
ensure
134+
raise @string
135+
end
136+
assert_raise_with_message(RuntimeError, @string) do
137+
meow
138+
end
139+
end
140+
81141
def test_errinfo_in_debug
82142
bug9568 = EnvUtil.labeled_class("[ruby-core:61091] [Bug #9568]", RuntimeError) do
83143
def to_s

0 commit comments

Comments
 (0)