summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Patterson <tenderlove@ruby-lang.org>2024-01-22 11:24:34 -0800
committerAaron Patterson <aaron.patterson@gmail.com>2024-01-22 12:02:03 -0800
commit7db683222528ca4850fed57529679e5aa14b0a88 (patch)
tree6e2b554e993347054cc6021c9ad51caf3df94643
parent1236cad92d509c299f0199c69ed752ea92f21023 (diff)
Fix compiling rescue + ensure
When we're compiling begin / rescue / ensure nodes, we need to "wrap" the code in the begin statements correctly. The wrapping is like this: (ensure code (rescue code (begin code))) This patch pulls the each leg in to its own function, then calls the appropriate wrapping function depending on whether there are ensure / rescue legs. Fixes: https://github.com/ruby/prism/issues/2221
-rw-r--r--prism_compile.c210
-rw-r--r--test/ruby/test_compile_prism.rb16
-rw-r--r--tool/prism_btests2
3 files changed, 131 insertions, 97 deletions
diff --git a/prism_compile.c b/prism_compile.c
index 4d139a57b3..359396567a 100644
--- a/prism_compile.c
+++ b/prism_compile.c
@@ -3362,6 +3362,111 @@ pm_compile_for_node_index(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *c
}
}
+static void
+pm_compile_rescue(rb_iseq_t *iseq, pm_begin_node_t *begin_node, LINK_ANCHOR *const ret, const uint8_t *src, int lineno, bool popped, pm_scope_node_t *scope_node)
+{
+ NODE dummy_line_node = generate_dummy_line_node(lineno, lineno);
+ pm_parser_t *parser = scope_node->parser;
+ LABEL *lstart = NEW_LABEL(lineno);
+ LABEL *lend = NEW_LABEL(lineno);
+ LABEL *lcont = NEW_LABEL(lineno);
+
+ pm_scope_node_t rescue_scope_node;
+ pm_scope_node_init((pm_node_t *)begin_node->rescue_clause, &rescue_scope_node, scope_node, parser);
+ rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(&rescue_scope_node,
+ rb_str_concat(rb_str_new2("rescue in"),
+ ISEQ_BODY(iseq)->location.label),
+ ISEQ_TYPE_RESCUE, 1);
+ pm_scope_node_destroy(&rescue_scope_node);
+
+ lstart->rescued = LABEL_RESCUE_BEG;
+ lend->rescued = LABEL_RESCUE_END;
+ ADD_LABEL(ret, lstart);
+ bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
+ ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
+ if (begin_node->statements) {
+ PM_COMPILE_NOT_POPPED((pm_node_t *)begin_node->statements);
+ }
+ else {
+ PM_PUTNIL;
+ }
+ ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
+
+ if (begin_node->else_clause) {
+ PM_POP_UNLESS_POPPED;
+ PM_COMPILE((pm_node_t *)begin_node->else_clause);
+ }
+
+ ADD_LABEL(ret, lend);
+ PM_NOP;
+ ADD_LABEL(ret, lcont);
+
+ PM_POP_IF_POPPED;
+ ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
+ ADD_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
+}
+
+static void
+pm_compile_ensure(rb_iseq_t *iseq, pm_begin_node_t *begin_node, LINK_ANCHOR *const ret, const uint8_t *src, int lineno, bool popped, pm_scope_node_t *scope_node)
+{
+ NODE dummy_line_node = generate_dummy_line_node(lineno, lineno);
+ pm_parser_t *parser = scope_node->parser;
+
+ LABEL *estart = NEW_LABEL(lineno);
+ LABEL *eend = NEW_LABEL(lineno);
+ LABEL *econt = NEW_LABEL(lineno);
+
+ struct ensure_range er;
+ struct iseq_compile_data_ensure_node_stack enl;
+ struct ensure_range *erange;
+
+ er.begin = estart;
+ er.end = eend;
+ er.next = 0;
+ push_ensure_entry(iseq, &enl, &er, (void *)begin_node->ensure_clause);
+
+ ADD_LABEL(ret, estart);
+ if (begin_node->rescue_clause) {
+ pm_compile_rescue(iseq, begin_node, ret, src, lineno, popped, scope_node);
+ }
+ else {
+ if (begin_node->statements) {
+ PM_COMPILE((pm_node_t *)begin_node->statements);
+ }
+ else {
+ PM_PUTNIL_UNLESS_POPPED;
+ }
+ }
+
+ ADD_LABEL(ret, eend);
+ ADD_LABEL(ret, econt);
+
+ pm_scope_node_t next_scope_node;
+ pm_scope_node_init((pm_node_t *)begin_node->ensure_clause, &next_scope_node, scope_node, parser);
+ rb_iseq_t * child_iseq = NEW_CHILD_ISEQ(&next_scope_node,
+ rb_str_new2("ensure in"),
+ ISEQ_TYPE_ENSURE, lineno);
+ pm_scope_node_destroy(&next_scope_node);
+
+ ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
+
+ erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
+ if (estart->link.next != &eend->link) {
+ while (erange) {
+ ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end, child_iseq, econt);
+ erange = erange->next;
+ }
+ }
+ ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
+
+ // Compile the ensure entry
+ pm_statements_node_t *statements = begin_node->ensure_clause->statements;
+ if (statements) {
+ PM_COMPILE((pm_node_t *)statements);
+ PM_POP_UNLESS_POPPED;
+ }
+}
+
/*
* Compiles a prism node into instruction sequences
*
@@ -3598,105 +3703,18 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret,
}
case PM_BEGIN_NODE: {
pm_begin_node_t *begin_node = (pm_begin_node_t *) node;
- rb_iseq_t *child_iseq;
-
- if (begin_node->rescue_clause) {
- LABEL *lstart = NEW_LABEL(lineno);
- LABEL *lend = NEW_LABEL(lineno);
- LABEL *lcont = NEW_LABEL(lineno);
-
- pm_scope_node_t rescue_scope_node;
- pm_scope_node_init((pm_node_t *)begin_node->rescue_clause, &rescue_scope_node, scope_node, parser);
- rb_iseq_t *rescue_iseq = NEW_CHILD_ISEQ(&rescue_scope_node,
- rb_str_concat(rb_str_new2("rescue in"),
- ISEQ_BODY(iseq)->location.label),
- ISEQ_TYPE_RESCUE, 1);
- pm_scope_node_destroy(&rescue_scope_node);
-
- lstart->rescued = LABEL_RESCUE_BEG;
- lend->rescued = LABEL_RESCUE_END;
- ADD_LABEL(ret, lstart);
- bool prev_in_rescue = ISEQ_COMPILE_DATA(iseq)->in_rescue;
- ISEQ_COMPILE_DATA(iseq)->in_rescue = true;
- if (begin_node->statements) {
- PM_COMPILE_NOT_POPPED((pm_node_t *)begin_node->statements);
- }
- else {
- PM_PUTNIL;
- }
- ISEQ_COMPILE_DATA(iseq)->in_rescue = prev_in_rescue;
- if (begin_node->else_clause) {
- PM_POP_UNLESS_POPPED;
- PM_COMPILE((pm_node_t *)begin_node->else_clause);
- }
-
- ADD_LABEL(ret, lend);
- PM_NOP;
- ADD_LABEL(ret, lcont);
-
- PM_POP_IF_POPPED;
- ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue_iseq, lcont);
- ADD_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, NULL, lstart);
- }
if (begin_node->ensure_clause) {
- LABEL *estart = NEW_LABEL(lineno);
- LABEL *eend = NEW_LABEL(lineno);
- LABEL *econt = NEW_LABEL(lineno);
-
- struct ensure_range er;
- struct iseq_compile_data_ensure_node_stack enl;
- struct ensure_range *erange;
-
- er.begin = estart;
- er.end = eend;
- er.next = 0;
- push_ensure_entry(iseq, &enl, &er, (void *)begin_node->ensure_clause);
-
- ADD_LABEL(ret, estart);
- if (!begin_node->rescue_clause) {
- if (begin_node->statements) {
- PM_COMPILE((pm_node_t *)begin_node->statements);
- }
- else {
- PM_PUTNIL_UNLESS_POPPED;
- }
- }
-
- ADD_LABEL(ret, eend);
- ADD_LABEL(ret, econt);
-
- if (!popped) {
- PM_NOP;
- }
-
- pm_scope_node_t next_scope_node;
- pm_scope_node_init((pm_node_t *)begin_node->ensure_clause, &next_scope_node, scope_node, parser);
- child_iseq = NEW_CHILD_ISEQ(&next_scope_node,
- rb_str_new2("ensure in"),
- ISEQ_TYPE_ENSURE, lineno);
- pm_scope_node_destroy(&next_scope_node);
-
- ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq;
-
- erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
- if (estart->link.next != &eend->link) {
- while (erange) {
- ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end, child_iseq, econt);
- erange = erange->next;
- }
- }
- ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
-
- // Compile the ensure entry
- pm_statements_node_t *statements = begin_node->ensure_clause->statements;
- if (statements) {
- PM_COMPILE((pm_node_t *)statements);
- PM_POP_UNLESS_POPPED;
- }
+ // Compiling the ensure clause will compile the rescue clause (if
+ // there is one), which will compile the begin statements
+ pm_compile_ensure(iseq, begin_node, ret, src, lineno, popped, scope_node);
}
-
- if (!begin_node->rescue_clause && !begin_node->ensure_clause) {
+ else if (begin_node->rescue_clause) {
+ // Compiling rescue will compile begin statements (if applicable)
+ pm_compile_rescue(iseq, begin_node, ret, src, lineno, popped, scope_node);
+ }
+ else {
+ // If there is neither ensure or rescue, the just compile statements
if (begin_node->statements) {
PM_COMPILE((pm_node_t *)begin_node->statements);
}
diff --git a/test/ruby/test_compile_prism.rb b/test/ruby/test_compile_prism.rb
index 8f42fb5851..29f0530462 100644
--- a/test/ruby/test_compile_prism.rb
+++ b/test/ruby/test_compile_prism.rb
@@ -1506,6 +1506,22 @@ a
CODE
end
+ def test_rescue_with_ensure
+ assert_prism_eval(<<-CODE)
+begin
+ begin
+ raise "a"
+ rescue
+ raise "b"
+ ensure
+ raise "c"
+ end
+rescue => e
+ e.message
+end
+ CODE
+ end
+
def test_required_kwarg_ordering
assert_prism_eval("def self.foo(a: 1, b:); [a, b]; end; foo(b: 2)")
end
diff --git a/tool/prism_btests b/tool/prism_btests
index 04e58ae85c..aa6806a2b1 100644
--- a/tool/prism_btests
+++ b/tool/prism_btests
@@ -28,7 +28,7 @@
../src/bootstraptest/test_yjit_30k_ifelse.rb
../src/bootstraptest/test_yjit_30k_methods.rb
../src/bootstraptest/test_yjit_rust_port.rb
-# ../src/bootstraptest/test_exception.rb
+../src/bootstraptest/test_exception.rb
# ../src/bootstraptest/test_insns.rb
# ../src/bootstraptest/test_method.rb
# ../src/bootstraptest/test_ractor.rb