diff options
author | Koichi Sasada <[email protected]> | 2021-04-22 10:44:52 +0900 |
---|---|---|
committer | Koichi Sasada <[email protected]> | 2021-04-22 11:33:39 +0900 |
commit | 609de71f043e8ba34f22b9993e444e2e5bb05709 (patch) | |
tree | 531d2d941add4c819ad975fb4d26b6e2b2d771bf /compile.c | |
parent | 5512353d97250e85c13bf10b9b32e750478cf474 (diff) |
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
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/4399
Diffstat (limited to 'compile.c')
-rw-r--r-- | compile.c | 34 |
1 files changed, 27 insertions, 7 deletions
@@ -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); |