From 90d9c20a0c0df5565d5f95d5e14c58331fa5922f Mon Sep 17 00:00:00 2001 From: Matt Valentine-House Date: Fri, 10 Nov 2023 14:51:14 +0000 Subject: [PATCH] [PRISM] Compile RescueNode --- prism_compile.c | 123 +++++++++++++++++++++++++++++--- test/ruby/test_compile_prism.rb | 64 +++++++++++++++++ 2 files changed, 176 insertions(+), 11 deletions(-) diff --git a/prism_compile.c b/prism_compile.c index 5efe391922894b..ff730411df9d80 100644 --- a/prism_compile.c +++ b/prism_compile.c @@ -1408,6 +1408,12 @@ pm_scope_node_init(const pm_node_t *node, pm_scope_node_t *scope, pm_scope_node_ scope->locals = cast->locals; break; } + case PM_RESCUE_NODE: { + pm_rescue_node_t *cast = (pm_rescue_node_t *)node; + scope->body = (pm_node_t *)cast->statements; + scope->local_depth_offset += 1; + break; + } case PM_SINGLETON_CLASS_NODE: { pm_singleton_class_node_t *cast = (pm_singleton_class_node_t *) node; scope->body = cast->body; @@ -1809,24 +1815,23 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, rb_iseq_t *child_iseq; LABEL *lstart = NEW_LABEL(lineno); LABEL *lend = NEW_LABEL(lineno); - - ADD_LABEL(ret, lstart); - if (begin_node->statements) { - PM_COMPILE((pm_node_t *)begin_node->statements); - } - else { - PM_PUTNIL_UNLESS_POPPED; - } - ADD_LABEL(ret, lend); + LABEL *lcont = NEW_LABEL(lineno); if (begin_node->ensure_clause) { + ADD_LABEL(ret, lstart); + if (begin_node->statements) { + PM_COMPILE((pm_node_t *)begin_node->statements); + } + else { + PM_PUTNIL_UNLESS_POPPED; + } + ADD_LABEL(ret, lend); pm_statements_node_t *statements = begin_node->ensure_clause->statements; if (statements) { PM_COMPILE((pm_node_t *)statements); PM_POP_UNLESS_POPPED; } - LABEL *lcont = NEW_LABEL(lineno); struct ensure_range er; struct iseq_compile_data_ensure_node_stack enl; struct ensure_range *erange; @@ -1853,6 +1858,53 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, } } } + + if (begin_node->rescue_clause) { + 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); + 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); + ADD_INSN(ret, &dummy_line_node, 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->rescue_clause && !begin_node->ensure_clause) { + ADD_LABEL(ret, lstart); + if (begin_node->statements) { + PM_COMPILE((pm_node_t *)begin_node->statements); + } + else { + PM_PUTNIL_UNLESS_POPPED; + } + ADD_LABEL(ret, lend); + } return; } case PM_BLOCK_ARGUMENT_NODE: { @@ -1881,7 +1933,6 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, ID method_id = pm_constant_id_lookup(scope_node, call_node->name); int flags = 0; struct rb_callinfo_kwarg *kw_arg = NULL; - if (call_node->receiver == NULL) { PM_PUTSELF; } else { @@ -3573,6 +3624,49 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, } return; } + case PM_RESCUE_NODE: { + LABEL *excep_match = NEW_LABEL(lineno); + LABEL *rescue_end = NEW_LABEL(lineno); + + pm_rescue_node_t *rescue_node = (pm_rescue_node_t *)node; + iseq_set_exception_local_table(iseq); + + pm_node_list_t exception_list = rescue_node->exceptions; + if (exception_list.size > 0) { + for (size_t i = 0; i < exception_list.size; i++) { + ADD_GETLOCAL(ret, &dummy_line_node, LVAR_ERRINFO, 0); + PM_COMPILE(exception_list.nodes[i]); + ADD_INSN1(ret, &dummy_line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE)); + ADD_INSN1(ret, &dummy_line_node, branchif, excep_match); + } + } else { + ADD_GETLOCAL(ret, &dummy_line_node, LVAR_ERRINFO, 0); + ADD_INSN1(ret, &dummy_line_node, putobject, rb_eStandardError); + ADD_INSN1(ret, &dummy_line_node, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE)); + ADD_INSN1(ret, &dummy_line_node, branchif, excep_match); + } + ADD_INSN1(ret, &dummy_line_node, jump, rescue_end); + + ADD_LABEL(ret, excep_match); + ADD_TRACE(ret, RUBY_EVENT_RESCUE); + if (rescue_node->reference) { + ADD_GETLOCAL(ret, &dummy_line_node, LVAR_ERRINFO, 0); + PM_COMPILE((pm_node_t *)rescue_node->reference); + } + if (rescue_node->statements) { + PM_COMPILE((pm_node_t *)rescue_node->statements); + } + ADD_INSN(ret, &dummy_line_node, leave); + ADD_LABEL(ret, rescue_end); + + if (rescue_node->consequent) { + PM_COMPILE((pm_node_t *)rescue_node->consequent); + } else { + ADD_GETLOCAL(ret, &dummy_line_node, 1, 0); + } + + return; + } case PM_RETURN_NODE: { pm_arguments_node_t *arguments = ((pm_return_node_t *)node)->arguments; @@ -3871,6 +3965,13 @@ pm_compile_node(rb_iseq_t *iseq, const pm_node_t *node, LINK_ANCHOR *const ret, ADD_INSN1(ret, &dummy_line_node, throw, INT2FIX(0)); return; } + case ISEQ_TYPE_RESCUE: { + iseq_set_exception_local_table(iseq); + PM_COMPILE((pm_node_t *)scope_node->ast_node); + ADD_INSN1(ret, &dummy_line_node, throw, INT2FIX(0)); + + return; + } default: if (scope_node->body) { PM_COMPILE((pm_node_t *)scope_node->body); diff --git a/test/ruby/test_compile_prism.rb b/test/ruby/test_compile_prism.rb index fcc1f849d51274..d62ee7c879e090 100644 --- a/test/ruby/test_compile_prism.rb +++ b/test/ruby/test_compile_prism.rb @@ -769,6 +769,70 @@ def test_RedoNode # ) end + def test_RescueNode + assert_prism_eval("begin; 1; rescue; 2; end") + assert_prism_eval(<<~CODE) + begin + 1 + rescue SyntaxError + 2 + end + CODE + assert_prism_eval(<<~CODE) + begin + 1 + raise 'boom' + rescue StandardError + 2 + end + CODE + assert_prism_eval(<<~CODE) + begin + a = 1 + rescue StandardError => e + end + CODE + assert_prism_eval(<<~CODE) + begin + 1 + rescue StandardError => e + e + rescue SyntaxError => f + f + else + 4 + end + CODE + assert_prism_eval(<<-CODE) + begin + a = 2 + rescue + a = 3 + end + a + CODE + assert_prism_eval(<<-CODE) + a = 1 + begin + a = 2 + rescue + a = 3 + end + a + CODE + assert_prism_eval(<<-CODE) + a = 1 + begin + b = 2 + raise "bang" + rescue + c = 3 + end + a + b + c + CODE + assert_prism_eval("begin; rescue; end") + end + def test_ReturnNode assert_prism_eval("def return_node; return 1; end") end