From b182f2a04520a0138992b27f9e6bfd15bdfd6f96 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Thu, 20 Jun 2024 23:56:03 +0900 Subject: [PATCH] fix sendfwd with `send` and `method_missing` combination with `send` method (optimized) or `method_missing` and forwarding send (`...`) needs to respect given `rb_forwarding_call_data`. Otherwize it causes critical error such as SEGV. --- bootstraptest/test_method.rb | 38 ++++++++++++++++++++++--------- vm_insnhelper.c | 43 +++++++++++++++++++++++++++++++----- 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/bootstraptest/test_method.rb b/bootstraptest/test_method.rb index 3929d69da4..846d8e36a9 100644 --- a/bootstraptest/test_method.rb +++ b/bootstraptest/test_method.rb @@ -1315,14 +1315,32 @@ assert_equal 'ok', %q{ } assert_equal 'ok', %q{ -def foo(a, b) = a + b -def bar(...) = foo(...) -bar(1, 2) -bar(1, 2) -begin - bar(1, 2, 3) - "ng" -rescue ArgumentError - "ok" -end + def foo(a, b) = a + b + def bar(...) = foo(...) + bar(1, 2) + bar(1, 2) + begin + bar(1, 2, 3) + "ng" + rescue ArgumentError + "ok" + end +} + +assert_equal 'ok', %q{ + class C + def foo(...) = :ok + def bar(...) = __send__(:foo, ...) + end + + C.new.bar +} + +assert_equal 'ok', %q{ + class C + def method_missing(...) = :ok + def foo(...) = xyzzy(...) + end + + C.new.foo } diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 33d39ab857..be43e737fe 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -3069,6 +3069,9 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling, const struct rb_callinfo *ci = calling->cd->ci; const struct rb_callcache *cc = calling->cc; + VM_ASSERT((vm_ci_argc(ci), 1)); + VM_ASSERT(vm_cc_cme(cc) != NULL); + if (UNLIKELY(!ISEQ_BODY(iseq)->param.flags.use_block && calling->block_handler != VM_BLOCK_HANDLER_NONE && !(vm_ci_flag(calling->cd->ci) & VM_CALL_SUPER))) { @@ -4235,10 +4238,23 @@ vm_call_symbol(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, } } - calling->cd = &(struct rb_call_data) { - .ci = &VM_CI_ON_STACK(mid, flags, argc, vm_ci_kwarg(ci)), - .cc = NULL, + struct rb_forwarding_call_data new_fcd = { + .cd = { + .ci = &VM_CI_ON_STACK(mid, flags, argc, vm_ci_kwarg(ci)), + .cc = NULL, + }, + .caller_ci = NULL, }; + + if (!(vm_ci_flag(ci) & VM_CALL_FORWARDING)) { + calling->cd = &new_fcd.cd; + } + else { + const struct rb_callinfo *caller_ci = ((struct rb_forwarding_call_data *)calling->cd)->caller_ci; + VM_ASSERT((vm_ci_argc(caller_ci), 1)); + new_fcd.caller_ci = caller_ci; + calling->cd = (struct rb_call_data *)&new_fcd; + } calling->cc = &VM_CC_ON_STACK(klass, vm_call_general, { .method_missing_reason = missing_reason }, @@ -4381,10 +4397,25 @@ vm_call_method_missing_body(rb_execution_context_t *ec, rb_control_frame_t *reg_ INC_SP(1); ec->method_missing_reason = reason; - calling->cd = &(struct rb_call_data) { - .ci = &VM_CI_ON_STACK(idMethodMissing, flag, argc, vm_ci_kwarg(orig_ci)), - .cc = NULL, + + struct rb_forwarding_call_data new_fcd = { + .cd = { + .ci = &VM_CI_ON_STACK(idMethodMissing, flag, argc, vm_ci_kwarg(orig_ci)), + .cc = NULL, + }, + .caller_ci = NULL, }; + + if (!(flag & VM_CALL_FORWARDING)) { + calling->cd = &new_fcd.cd; + } + else { + const struct rb_callinfo *caller_ci = ((struct rb_forwarding_call_data *)calling->cd)->caller_ci; + VM_ASSERT((vm_ci_argc(caller_ci), 1)); + new_fcd.caller_ci = caller_ci; + calling->cd = (struct rb_call_data *)&new_fcd; + } + calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, rb_callable_method_entry_without_refinements(CLASS_OF(calling->recv), idMethodMissing, NULL)); return vm_call_method(ec, reg_cfp, calling);