YJIT: Reject keywords hash in -1 arity cfunc splat support

`test_keyword.rb` caught this issue. Just need to run with `threshold=1`
This commit is contained in:
Alan Wu 2024-02-28 14:27:16 -05:00
parent 4b92b60f0b
commit 558b58d330
4 changed files with 28 additions and 18 deletions

View File

@ -4658,3 +4658,19 @@ assert_equal '[[1, 2, 3], [0, 2, 3], [1, 2, 3], [2, 2, 3], [], [], [{}]]', %q{
test_cfunc_vargs_splat(Foo.new, Array, Hash.ruby2_keywords_hash({})) test_cfunc_vargs_splat(Foo.new, Array, Hash.ruby2_keywords_hash({}))
} }
# Class#new (arity=-1), splat, and ruby2_keywords
assert_equal '[0, {1=>1}]', %q{
class KwInit
attr_reader :init_args
def initialize(x = 0, **kw)
@init_args = [x, kw]
end
end
def test(klass, args)
klass.new(*args).init_args
end
test(KwInit, [Hash.ruby2_keywords_hash({1 => 1})])
}

22
yjit.c
View File

@ -903,13 +903,22 @@ rb_yjit_splat_varg_checks(VALUE *sp, VALUE splat_array, rb_control_frame_t *cfp)
// Would we overflow if we put the contents of the array onto the stack? // Would we overflow if we put the contents of the array onto the stack?
if (sp + len > (VALUE *)(cfp - 2)) return Qfalse; if (sp + len > (VALUE *)(cfp - 2)) return Qfalse;
// Reject keywords hash since that requires duping it sometimes
if (len > 0) {
VALUE last_hash = RARRAY_AREF(splat_array, len - 1);
if (RB_TYPE_P(last_hash, T_HASH) &&
FL_TEST_RAW(last_hash, RHASH_PASS_AS_KEYWORDS)) {
return Qfalse;
}
}
return Qtrue; return Qtrue;
} }
// Push array elements to the stack for a C method that has a variable number // Push array elements to the stack for a C method that has a variable number
// of parameters. Returns the number of arguments the splat array contributes. // of parameters. Returns the number of arguments the splat array contributes.
int int
rb_yjit_splat_varg_cfunc(VALUE *stack_splat_array, bool sole_splat) rb_yjit_splat_varg_cfunc(VALUE *stack_splat_array)
{ {
VALUE splat_array = *stack_splat_array; VALUE splat_array = *stack_splat_array;
int len; int len;
@ -918,17 +927,6 @@ rb_yjit_splat_varg_cfunc(VALUE *stack_splat_array, bool sole_splat)
RUBY_ASSERT(RB_TYPE_P(splat_array, T_ARRAY)); RUBY_ASSERT(RB_TYPE_P(splat_array, T_ARRAY));
len = (int)RARRAY_LEN(splat_array); len = (int)RARRAY_LEN(splat_array);
// If this is a splat call without any keyword arguments, exclude the
// ruby2_keywords hash if it's empty
if (sole_splat && len > 0) {
VALUE last_hash = RARRAY_AREF(splat_array, len - 1);
if (RB_TYPE_P(last_hash, T_HASH) &&
FL_TEST_RAW(last_hash, RHASH_PASS_AS_KEYWORDS) &&
RHASH_EMPTY_P(last_hash)) {
len--;
}
}
// Push the contents of the array onto the stack // Push the contents of the array onto the stack
MEMCPY(stack_splat_array, RARRAY_CONST_PTR(splat_array), VALUE, len); MEMCPY(stack_splat_array, RARRAY_CONST_PTR(splat_array), VALUE, len);

View File

@ -6324,9 +6324,8 @@ fn gen_send_cfunc(
// Push a dynamic number of items from the splat array to the stack when calling a vargs method // Push a dynamic number of items from the splat array to the stack when calling a vargs method
let dynamic_splat_size = if variable_splat { let dynamic_splat_size = if variable_splat {
asm_comment!(asm, "variable length splat"); asm_comment!(asm, "variable length splat");
let just_splat = usize::from(!kw_splat && kw_arg.is_null()).into();
let stack_splat_array = asm.lea(asm.stack_opnd(0)); let stack_splat_array = asm.lea(asm.stack_opnd(0));
Some(asm.ccall(rb_yjit_splat_varg_cfunc as _, vec![stack_splat_array, just_splat])) Some(asm.ccall(rb_yjit_splat_varg_cfunc as _, vec![stack_splat_array]))
} else { } else {
None None
}; };

View File

@ -1199,10 +1199,7 @@ extern "C" {
splat_array: VALUE, splat_array: VALUE,
cfp: *mut rb_control_frame_t, cfp: *mut rb_control_frame_t,
) -> VALUE; ) -> VALUE;
pub fn rb_yjit_splat_varg_cfunc( pub fn rb_yjit_splat_varg_cfunc(stack_splat_array: *mut VALUE) -> ::std::os::raw::c_int;
stack_splat_array: *mut VALUE,
sole_splat: bool,
) -> ::std::os::raw::c_int;
pub fn rb_yjit_dump_iseq_loc(iseq: *const rb_iseq_t, insn_idx: u32); pub fn rb_yjit_dump_iseq_loc(iseq: *const rb_iseq_t, insn_idx: u32);
pub fn rb_yjit_iseq_inspect(iseq: *const rb_iseq_t) -> *mut ::std::os::raw::c_char; pub fn rb_yjit_iseq_inspect(iseq: *const rb_iseq_t) -> *mut ::std::os::raw::c_char;
pub fn rb_FL_TEST(obj: VALUE, flags: VALUE) -> VALUE; pub fn rb_FL_TEST(obj: VALUE, flags: VALUE) -> VALUE;