summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoichi Sasada <ko1@atdot.net>2021-04-22 10:44:52 +0900
committerKoichi Sasada <ko1@atdot.net>2021-04-22 11:33:39 +0900
commit609de71f043e8ba34f22b9993e444e2e5bb05709 (patch)
tree531d2d941add4c819ad975fb4d26b6e2b2d771bf
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
-rw-r--r--compile.c34
-rw-r--r--iseq.h1
-rw-r--r--test/ruby/test_exception.rb60
3 files changed, 88 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);
diff --git a/iseq.h b/iseq.h
index 6a6c69a114..904b891427 100644
--- a/iseq.h
+++ b/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;
diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb
index 087bcda5ac..0a2a31281d 100644
--- a/test/ruby/test_exception.rb
+++ b/test/ruby/test_exception.rb
@@ -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