[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]