Skip to content

Commit b182f2a

Browse files
committed
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.
1 parent f5fd87b commit b182f2a

File tree

2 files changed

+65
-16
lines changed

2 files changed

+65
-16
lines changed

bootstraptest/test_method.rb

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,14 +1315,32 @@ class A; attr_reader :iv; def initialize(...) = @iv = "ok"; end
13151315
}
13161316

13171317
assert_equal 'ok', %q{
1318-
def foo(a, b) = a + b
1319-
def bar(...) = foo(...)
1320-
bar(1, 2)
1321-
bar(1, 2)
1322-
begin
1323-
bar(1, 2, 3)
1324-
"ng"
1325-
rescue ArgumentError
1326-
"ok"
1327-
end
1318+
def foo(a, b) = a + b
1319+
def bar(...) = foo(...)
1320+
bar(1, 2)
1321+
bar(1, 2)
1322+
begin
1323+
bar(1, 2, 3)
1324+
"ng"
1325+
rescue ArgumentError
1326+
"ok"
1327+
end
1328+
}
1329+
1330+
assert_equal 'ok', %q{
1331+
class C
1332+
def foo(...) = :ok
1333+
def bar(...) = __send__(:foo, ...)
1334+
end
1335+
1336+
C.new.bar
1337+
}
1338+
1339+
assert_equal 'ok', %q{
1340+
class C
1341+
def method_missing(...) = :ok
1342+
def foo(...) = xyzzy(...)
1343+
end
1344+
1345+
C.new.foo
13281346
}

vm_insnhelper.c

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3069,6 +3069,9 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
30693069
const struct rb_callinfo *ci = calling->cd->ci;
30703070
const struct rb_callcache *cc = calling->cc;
30713071

3072+
VM_ASSERT((vm_ci_argc(ci), 1));
3073+
VM_ASSERT(vm_cc_cme(cc) != NULL);
3074+
30723075
if (UNLIKELY(!ISEQ_BODY(iseq)->param.flags.use_block &&
30733076
calling->block_handler != VM_BLOCK_HANDLER_NONE &&
30743077
!(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,
42354238
}
42364239
}
42374240

4238-
calling->cd = &(struct rb_call_data) {
4239-
.ci = &VM_CI_ON_STACK(mid, flags, argc, vm_ci_kwarg(ci)),
4240-
.cc = NULL,
4241+
struct rb_forwarding_call_data new_fcd = {
4242+
.cd = {
4243+
.ci = &VM_CI_ON_STACK(mid, flags, argc, vm_ci_kwarg(ci)),
4244+
.cc = NULL,
4245+
},
4246+
.caller_ci = NULL,
42414247
};
4248+
4249+
if (!(vm_ci_flag(ci) & VM_CALL_FORWARDING)) {
4250+
calling->cd = &new_fcd.cd;
4251+
}
4252+
else {
4253+
const struct rb_callinfo *caller_ci = ((struct rb_forwarding_call_data *)calling->cd)->caller_ci;
4254+
VM_ASSERT((vm_ci_argc(caller_ci), 1));
4255+
new_fcd.caller_ci = caller_ci;
4256+
calling->cd = (struct rb_call_data *)&new_fcd;
4257+
}
42424258
calling->cc = &VM_CC_ON_STACK(klass,
42434259
vm_call_general,
42444260
{ .method_missing_reason = missing_reason },
@@ -4381,10 +4397,25 @@ vm_call_method_missing_body(rb_execution_context_t *ec, rb_control_frame_t *reg_
43814397
INC_SP(1);
43824398

43834399
ec->method_missing_reason = reason;
4384-
calling->cd = &(struct rb_call_data) {
4385-
.ci = &VM_CI_ON_STACK(idMethodMissing, flag, argc, vm_ci_kwarg(orig_ci)),
4386-
.cc = NULL,
4400+
4401+
struct rb_forwarding_call_data new_fcd = {
4402+
.cd = {
4403+
.ci = &VM_CI_ON_STACK(idMethodMissing, flag, argc, vm_ci_kwarg(orig_ci)),
4404+
.cc = NULL,
4405+
},
4406+
.caller_ci = NULL,
43874407
};
4408+
4409+
if (!(flag & VM_CALL_FORWARDING)) {
4410+
calling->cd = &new_fcd.cd;
4411+
}
4412+
else {
4413+
const struct rb_callinfo *caller_ci = ((struct rb_forwarding_call_data *)calling->cd)->caller_ci;
4414+
VM_ASSERT((vm_ci_argc(caller_ci), 1));
4415+
new_fcd.caller_ci = caller_ci;
4416+
calling->cd = (struct rb_call_data *)&new_fcd;
4417+
}
4418+
43884419
calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }},
43894420
rb_callable_method_entry_without_refinements(CLASS_OF(calling->recv), idMethodMissing, NULL));
43904421
return vm_call_method(ec, reg_cfp, calling);

0 commit comments

Comments
 (0)