diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index f9cdca6f28..bd44f3ac3e 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -2680,6 +2680,22 @@ assert_equal '[1, 2]', %q{ expandarray_redefined_nilclass } +assert_equal 'not_array', %q{ + def expandarray_not_array(obj) + a, = obj + a + end + + obj = Object.new + def obj.method_missing(m, *args, &block) + return [:not_array] if m == :to_ary + super + end + + expandarray_not_array(obj) + expandarray_not_array(obj) +} + assert_equal '[1, 2, nil]', %q{ def expandarray_rhs_too_small a, b, c = [1, 2] diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 9eeccddf6c..a426ad0773 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -2258,7 +2258,8 @@ fn gen_expandarray( let comptime_recv = jit.peek_at_stack(&asm.ctx, 0); - // If the comptime receiver is not an array + // If the comptime receiver is not an array, speculate for when the `rb_check_array_type()` + // conversion returns nil and without side-effects (e.g. arbitrary method calls). if !unsafe { RB_TYPE_P(comptime_recv, RUBY_T_ARRAY) } { // at compile time, ensure to_ary is not defined let target_cme = unsafe { rb_callable_method_entry_or_negative(comptime_recv.class_of(), ID!(to_ary)) }; @@ -2270,6 +2271,13 @@ fn gen_expandarray( return None; } + // Bail when method_missing is defined to avoid generating code to call it. + // Also, for simplicity, bail when BasicObject#method_missing has been removed. + if !assume_method_basic_definition(jit, asm, comptime_recv.class_of(), ID!(method_missing)) { + gen_counter_incr(jit, asm, Counter::expandarray_method_missing); + return None; + } + // invalidate compile block if to_ary is later defined jit.assume_method_lookup_stable(asm, target_cme); diff --git a/yjit/src/cruby.rs b/yjit/src/cruby.rs index 0d9e3b74da..cfaf48c3f0 100644 --- a/yjit/src/cruby.rs +++ b/yjit/src/cruby.rs @@ -816,6 +816,7 @@ pub(crate) mod ids { def_ids! { name: NULL content: b"" name: respond_to_missing content: b"respond_to_missing?" + name: method_missing content: b"method_missing" name: to_ary content: b"to_ary" name: to_s content: b"to_s" name: eq content: b"==" diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs index 817c842cf4..e8ce7b8b35 100644 --- a/yjit/src/stats.rs +++ b/yjit/src/stats.rs @@ -496,6 +496,7 @@ make_counters! { expandarray_postarg, expandarray_not_array, expandarray_to_ary, + expandarray_method_missing, expandarray_chain_max_depth, // getblockparam