summaryrefslogtreecommitdiff
path: root/compile.c
diff options
context:
space:
mode:
authorKoichi Sasada <[email protected]>2021-04-22 10:44:52 +0900
committerKoichi Sasada <[email protected]>2021-04-22 11:33:39 +0900
commit609de71f043e8ba34f22b9993e444e2e5bb05709 (patch)
tree531d2d941add4c819ad975fb4d26b6e2b2d771bf /compile.c
parent5512353d97250e85c13bf10b9b32e750478cf474 (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.c34
1 files changed, 27 insertions, 7 deletions
diff --git a/compile.c b/compile.c
index e6dfc76d9a..1cabb8cccd 100644
--- a/compile.c
+++ b/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);