Skip to content

Commit 5f55c9c

Browse files
rwstaunerXrXr
andcommitted
YJIT: Abort expandarray optimization if method_missing is defined
Fixes: [Bug #21707] [AW: rewrote comments] Co-authored-by: Alan Wu <[email protected]>
1 parent c42c6c2 commit 5f55c9c

File tree

4 files changed

+27
-1
lines changed

4 files changed

+27
-1
lines changed

bootstraptest/test_yjit.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2680,6 +2680,22 @@ def expandarray_redefined_nilclass
26802680
expandarray_redefined_nilclass
26812681
}
26822682

2683+
assert_equal 'not_array', %q{
2684+
def expandarray_not_array(obj)
2685+
a, = obj
2686+
a
2687+
end
2688+
2689+
obj = Object.new
2690+
def obj.method_missing(m, *args, &block)
2691+
return [:not_array] if m == :to_ary
2692+
super
2693+
end
2694+
2695+
expandarray_not_array(obj)
2696+
expandarray_not_array(obj)
2697+
}
2698+
26832699
assert_equal '[1, 2, nil]', %q{
26842700
def expandarray_rhs_too_small
26852701
a, b, c = [1, 2]

yjit/src/codegen.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2258,7 +2258,8 @@ fn gen_expandarray(
22582258

22592259
let comptime_recv = jit.peek_at_stack(&asm.ctx, 0);
22602260

2261-
// If the comptime receiver is not an array
2261+
// If the comptime receiver is not an array, speculate for when the `rb_check_array_type()`
2262+
// conversion returns nil and without side-effects (e.g. arbitrary method calls).
22622263
if !unsafe { RB_TYPE_P(comptime_recv, RUBY_T_ARRAY) } {
22632264
// at compile time, ensure to_ary is not defined
22642265
let target_cme = unsafe { rb_callable_method_entry_or_negative(comptime_recv.class_of(), ID!(to_ary)) };
@@ -2270,6 +2271,13 @@ fn gen_expandarray(
22702271
return None;
22712272
}
22722273

2274+
// Bail when method_missing is defined to avoid generating code to call it.
2275+
// Also, for simplicity, bail when BasicObject#method_missing has been removed.
2276+
if !assume_method_basic_definition(jit, asm, comptime_recv.class_of(), ID!(method_missing)) {
2277+
gen_counter_incr(jit, asm, Counter::expandarray_method_missing);
2278+
return None;
2279+
}
2280+
22732281
// invalidate compile block if to_ary is later defined
22742282
jit.assume_method_lookup_stable(asm, target_cme);
22752283

yjit/src/cruby.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,7 @@ pub(crate) mod ids {
816816
def_ids! {
817817
name: NULL content: b""
818818
name: respond_to_missing content: b"respond_to_missing?"
819+
name: method_missing content: b"method_missing"
819820
name: to_ary content: b"to_ary"
820821
name: to_s content: b"to_s"
821822
name: eq content: b"=="

yjit/src/stats.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ make_counters! {
496496
expandarray_postarg,
497497
expandarray_not_array,
498498
expandarray_to_ary,
499+
expandarray_method_missing,
499500
expandarray_chain_max_depth,
500501

501502
// getblockparam

0 commit comments

Comments
 (0)