YJIT: Bail out if proc would be stored above stack top

Fixes [Bug #21266].
This commit is contained in:
Randy Stauner 2025-12-11 15:56:16 -07:00 committed by Alan Wu
parent f3b9509b52
commit 9168cad4d6
Notes: git 2025-12-16 04:05:55 +00:00
4 changed files with 28 additions and 0 deletions

View File

@ -4889,6 +4889,16 @@ assert_equal '[:ok, :ok, :ok, :ok, :ok]', %q{
tests
}
# regression test for splat with &proc{} when the target has rest (Bug #21266)
assert_equal '[]', %q{
def foo(args) = bar(*args, &proc { _1 })
def bar(_, _, _, _, *rest) = yield rest
GC.stress = true
foo([1,2,3,4])
foo([1,2,3,4])
}
# regression test for invalidating an empty block
assert_equal '0', %q{
def foo = (* = 1).pred

View File

@ -1772,6 +1772,18 @@ class TestYJIT < Test::Unit::TestCase
RUBY
end
def test_proc_block_with_kwrest
# When the bug was present this required --yjit-stats to trigger.
assert_compiles(<<~RUBY, result: {extra: 5})
def foo = bar(w: 1, x: 2, y: 3, z: 4, extra: 5, &proc { _1 })
def bar(w:, x:, y:, z:, **kwrest) = yield kwrest
GC.stress = true
foo
foo
RUBY
end
private
def code_gc_helpers

View File

@ -7896,6 +7896,11 @@ fn gen_send_iseq(
gen_counter_incr(jit, asm, Counter::send_iseq_clobbering_block_arg);
return None;
}
if iseq_has_rest || has_kwrest {
// The proc would be stored above the current stack top, where GC can't see it
gen_counter_incr(jit, asm, Counter::send_iseq_block_arg_gc_unsafe);
return None;
}
let proc = asm.stack_pop(1); // Pop first, as argc doesn't account for the block arg
let callee_specval = asm.ctx.sp_opnd(callee_specval);
asm.store(callee_specval, proc);

View File

@ -354,6 +354,7 @@ make_counters! {
send_iseq_arity_error,
send_iseq_block_arg_type,
send_iseq_clobbering_block_arg,
send_iseq_block_arg_gc_unsafe,
send_iseq_complex_discard_extras,
send_iseq_leaf_builtin_block_arg_block_param,
send_iseq_kw_splat_non_nil,