Skip to content

Commit

Permalink
fix sendfwd with send and method_missing
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ko1 committed Jun 20, 2024
1 parent f5fd87b commit b182f2a
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 16 deletions.
38 changes: 28 additions & 10 deletions bootstraptest/test_method.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1315,14 1315,32 @@ class A; attr_reader :iv; def initialize(...) = @iv = "ok"; end
}

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
}
43 changes: 37 additions & 6 deletions vm_insnhelper.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))) {
Expand Down Expand Up @@ -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 },
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit b182f2a

Please sign in to comment.