ruby-changes:65917
From: Koichi <ko1@a...>
Date: Thu, 22 Apr 2021 11:33:56 +0900 (JST)
Subject: [ruby-changes:65917] 609de71f04 (master): fix raise in exception with jump
https://git.ruby-lang.org/ruby.git/commit/?id=609de71f04 From 609de71f043e8ba34f22b9993e444e2e5bb05709 Mon Sep 17 00:00:00 2001 From: Koichi Sasada <ko1@a...> Date: Thu, 22 Apr 2021 10:44:52 +0900 Subject: 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 --- compile.c | 34 +++++++++++++++++++------ iseq.h | 1 + test/ruby/test_exception.rb | 60 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 7 deletions(-) diff --git a/compile.c b/compile.c index e6dfc76..1cabb8c 100644 --- a/compile.c +++ b/compile.c @@ -5356,9 +5356,22 @@ add_ensure_range(rb_iseq_t *iseq, struct ensure_range *erange, https://github.com/ruby/ruby/blob/trunk/compile.c#L5356 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 https://github.com/ruby/ruby/blob/trunk/compile.c#L6863 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 https://github.com/ruby/ruby/blob/trunk/compile.c#L6922 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 https://github.com/ruby/ruby/blob/trunk/compile.c#L6935 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 https://github.com/ruby/ruby/blob/trunk/compile.c#L6995 { 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 https://github.com/ruby/ruby/blob/trunk/compile.c#L7007 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, https://github.com/ruby/ruby/blob/trunk/compile.c#L7093 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, https://github.com/ruby/ruby/blob/trunk/compile.c#L7261 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); diff --git a/iseq.h b/iseq.h index 6a6c69a..904b891 100644 --- a/iseq.h +++ b/iseq.h @@ -101,6 +101,7 @@ struct iseq_compile_data { https://github.com/ruby/ruby/blob/trunk/iseq.h#L101 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; diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb index 087bcda..0a2a312 100644 --- a/test/ruby/test_exception.rb +++ b/test/ruby/test_exception.rb @@ -78,6 +78,66 @@ class TestException < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_exception.rb#L78 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 -- cgit v1.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/