diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index 6b7d4839265d04..4d08ce70f18b07 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -4786,3 +4786,20 @@ def tests tests } + +# test String#stebyte with arguments that need conversion +assert_equal "abc", %q{ + str = +"a00" + def change_bytes(str, one, two) + str.setbyte(one, "b".ord) + str.setbyte(2, two) + end + + to_int_1 = Object.new + to_int_99 = Object.new + def to_int_1.to_int = 1 + def to_int_99.to_int = 99 + + change_bytes(str, to_int_1, to_int_99) + str +} diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index b047aa3310ae69..6233730c274cf5 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -5718,19 +5718,29 @@ fn jit_rb_str_getbyte( fn jit_rb_str_setbyte( jit: &mut JITState, asm: &mut Assembler, - _ocb: &mut OutlinedCb, + ocb: &mut OutlinedCb, _ci: *const rb_callinfo, cme: *const rb_callable_method_entry_t, _block: Option, _argc: i32, _known_recv_class: Option, ) -> bool { + // Check that arguments are fixnums since jit_prepare_lazy_frame_call() + // requires rb_str_setbyte() to be leaf or raise, but it tries to + // convert non-integer arguments by calling #to_int. + if !matches!(asm.ctx.two_fixnums_on_stack(jit), Some(true)) { + return false; + } + // Raises when index is out of range. Lazily push a frame in that case. if !jit_prepare_lazy_frame_call(jit, asm, cme, StackOpnd(2)) { return false; } asm_comment!(asm, "String#setbyte"); + // Check that arguments are fixnums + guard_two_fixnums(jit, asm, ocb); + let value = asm.stack_opnd(0); let index = asm.stack_opnd(1); let recv = asm.stack_opnd(2);