summaryrefslogtreecommitdiff
path: root/vm_insnhelper.c
diff options
context:
space:
mode:
authorKoichi Sasada <[email protected]>2024-06-20 23:56:03 +0900
committerKoichi Sasada <[email protected]>2024-06-21 00:43:48 +0900
commitb182f2a04520a0138992b27f9e6bfd15bdfd6f96 (patch)
tree3bcb5f604c81cfa42fa450dbee3c2e3876e20299 /vm_insnhelper.c
parentf5fd87b695a0e3b696651503d4eceaa17876fab2 (diff)
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.
Diffstat (limited to 'vm_insnhelper.c')
-rw-r--r--vm_insnhelper.c43
1 files changed, 37 insertions, 6 deletions
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);