From ed2fbb3612b5809d211f99772587dbdc75214e7e Mon Sep 17 00:00:00 2001 From: "NARUSE, Yui" Date: Thu, 21 Mar 2024 18:00:32 +0900 Subject: [PATCH 1/2] merge revision(s) bbd249e351af7e4929b518a5de73a832b5617273: [Backport #20192] YJIT: Properly reject keyword splat with `yield` We don't have support for keyword splat anywhere, but we tried to compile these anyways in case of `invokeblock`. This led to bad things happening such as passing the wrong value and passing a hash into rb_yjit_array_len(), which raised in the middle of compilation. [Bug #20192] --- bootstraptest/test_yjit.rb | 7 +++++++ version.h | 2 +- yjit/src/codegen.rs | 9 +++++++++ yjit/src/stats.rs | 2 ++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index c0f382fc37dcda..e9a1e7c6eabb0d 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -11,6 +11,13 @@ def call_foo call_foo } +# regression test for keyword splat with yield +assert_equal 'nil', %q{ + def splat_kw(kwargs) = yield(**kwargs) + + splat_kw({}) { _1 }.inspect +} + # regression test for arity check with splat assert_equal '[:ae, :ae]', %q{ def req_one(a_, b_ = 1) = raise diff --git a/version.h b/version.h index 5c45d2229c0d3c..499dc4ae20cbb3 100644 --- a/version.h +++ b/version.h @@ -11,7 +11,7 @@ # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR #define RUBY_VERSION_TEENY 0 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR -#define RUBY_PATCHLEVEL 19 +#define RUBY_PATCHLEVEL 20 #include "ruby/version.h" #include "ruby/internal/abi.h" diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 75986dedb17a5b..df98fcdf0860af 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -6009,6 +6009,7 @@ fn gen_send_iseq( exit_if_tail_call(asm, ci)?; exit_if_has_post(asm, iseq)?; exit_if_has_kwrest(asm, iseq)?; + exit_if_kw_splat(asm, flags)?; exit_if_splat_and_ruby2_keywords(asm, jit, flags)?; exit_if_has_rest_and_captured(asm, iseq_has_rest, captured_opnd)?; exit_if_has_rest_and_supplying_kws(asm, iseq_has_rest, iseq, supplying_kws)?; @@ -6139,6 +6140,9 @@ fn gen_send_iseq( let array = jit.peek_at_stack(&asm.ctx, if block_arg { 1 } else { 0 }) ; let array_length = if array == Qnil { 0 + } else if unsafe { !RB_TYPE_P(array, RUBY_T_ARRAY) } { + gen_counter_incr(asm, Counter::send_iseq_splat_not_array); + return None; } else { unsafe { rb_yjit_array_len(array) as u32} }; @@ -6820,6 +6824,11 @@ fn exit_if_has_kwrest(asm: &mut Assembler, iseq: *const rb_iseq_t) -> Option<()> exit_if(asm, unsafe { get_iseq_flags_has_kwrest(iseq) }, Counter::send_iseq_has_kwrest) } +#[must_use] +fn exit_if_kw_splat(asm: &mut Assembler, flags: u32) -> Option<()> { + exit_if(asm, flags & VM_CALL_KW_SPLAT != 0, Counter::send_iseq_kw_splat) +} + #[must_use] fn exit_if_splat_and_ruby2_keywords(asm: &mut Assembler, jit: &mut JITState, flags: u32) -> Option<()> { // In order to handle backwards compatibility between ruby 3 and 2 diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs index dcaa9af2b58495..eab3db010fb54b 100644 --- a/yjit/src/stats.rs +++ b/yjit/src/stats.rs @@ -331,6 +331,7 @@ make_counters! { send_iseq_clobbering_block_arg, send_iseq_leaf_builtin_block_arg_block_param, send_iseq_only_keywords, + send_iseq_kw_splat, send_iseq_kwargs_req_and_opt_missing, send_iseq_kwargs_mismatch, send_iseq_has_post, @@ -338,6 +339,7 @@ make_counters! { send_iseq_has_no_kw, send_iseq_accepts_no_kwarg, send_iseq_materialized_block, + send_iseq_splat_not_array, send_iseq_splat_with_opt, send_iseq_splat_with_kw, send_iseq_missing_optional_kw, From 16ec27c73fe1b3fb1d62bfb2ea2f55b28d09511a Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 28 May 2024 14:51:38 -0700 Subject: [PATCH 2/2] Skip a new test for RJIT --- bootstraptest/test_yjit.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index e9a1e7c6eabb0d..e831a9d550f3c2 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -16,7 +16,7 @@ def call_foo def splat_kw(kwargs) = yield(**kwargs) splat_kw({}) { _1 }.inspect -} +} unless rjit_enabled? # Not yet working on RJIT # regression test for arity check with splat assert_equal '[:ae, :ae]', %q{