diff options
author | NARUSE, Yui <naruse@airemix.jp> | 2023-03-15 16:36:32 +0900 |
---|---|---|
committer | NARUSE, Yui <naruse@airemix.jp> | 2023-03-15 16:36:32 +0900 |
commit | b73a07359758a9034996752e981e09ddaffe8d87 (patch) | |
tree | 242744cff549124d9e351c3d912591866a853db6 | |
parent | db28f7003f7d49cfa13871c38d10c1967282ca6b (diff) | |
download | ruby-b73a07359758a9034996752e981e09ddaffe8d87.tar.gz |
merge revision(s) 0eb634ae73cb327ede833b72492f912792a4a9d5: [Backport #19464]
YJIT: Detect and reject `send(:alias_for_send, :foo)`
Previously, YJIT failed to put the stack into the correct shape when
`BasicObject#send` calls an alias method for the send method itself.
This can manifest as strange `NoMethodError`s in the final non-send
receiver, as [seen][1] with the kt-paperclip gem. I also found a case
where it makes YJIT fail the stack size assertion while compiling
`leave`.
YJIT's `BasicObject#__send__` implementation already rejects sends to
`send`, but didn't detect sends to aliases of `send`. Adjust the
detection and reject these cases.
Fixes [Bug #19464]
[1]: https://github.com/Shopify/yjit/issues/306
---
test/ruby/test_yjit.rb | 20 ++++++++++++++++++++
yjit/src/codegen.rs | 25 ++++++++++---------------
2 files changed, 30 insertions(+), 15 deletions(-)
-rw-r--r-- | test/ruby/test_yjit.rb | 20 | ||||
-rw-r--r-- | version.h | 2 | ||||
-rw-r--r-- | yjit/src/codegen.rs | 25 |
3 files changed, 31 insertions, 16 deletions
diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb index 2ea915bc0d..e4080595b0 100644 --- a/test/ruby/test_yjit.rb +++ b/test/ruby/test_yjit.rb @@ -1121,6 +1121,26 @@ class TestYJIT < Test::Unit::TestCase RUBY end + def test_nested_send + #[Bug #19464] + assert_compiles(<<~RUBY, result: [:ok, :ok]) + klass = Class.new do + class << self + alias_method :my_send, :send + + def bar = :ok + + def foo = bar + end + end + + with_break = -> { break klass.send(:my_send, :foo) } + wo_break = -> { klass.send(:my_send, :foo) } + + [with_break[], wo_break[]] + RUBY + end + private def code_gc_helpers @@ -11,7 +11,7 @@ # define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR #define RUBY_VERSION_TEENY 1 #define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR -#define RUBY_PATCHLEVEL 39 +#define RUBY_PATCHLEVEL 40 #include "ruby/version.h" #include "ruby/internal/abi.h" diff --git a/yjit/src/codegen.rs b/yjit/src/codegen.rs index 8c5b413286..687205e10f 100644 --- a/yjit/src/codegen.rs +++ b/yjit/src/codegen.rs @@ -5822,7 +5822,6 @@ fn gen_send_general( let opt_type = unsafe { get_cme_def_body_optimized_type(cme) }; match opt_type { OPTIMIZED_METHOD_TYPE_SEND => { - // This is for method calls like `foo.send(:bar)` // The `send` method does not get its own stack frame. // instead we look up the method and call it, @@ -5830,6 +5829,16 @@ fn gen_send_general( let starting_context = ctx.clone(); + // Reject nested cases such as `send(:send, :alias_for_send, :foo))`. + // We would need to do some stack manipulation here or keep track of how + // many levels deep we need to stack manipulate. Because of how exits + // currently work, we can't do stack manipulation until we will no longer + // side exit. + if flags & VM_CALL_OPT_SEND != 0 { + gen_counter_incr!(asm, send_send_nested); + return CantCompile; + } + if argc == 0 { gen_counter_incr!(asm, send_send_wrong_args); return CantCompile; @@ -5856,20 +5865,6 @@ fn gen_send_general( return CantCompile; } - // We aren't going to handle `send(send(:foo))`. We would need to - // do some stack manipulation here or keep track of how many levels - // deep we need to stack manipulate - // Because of how exits currently work, we can't do stack manipulation - // until we will no longer side exit. - let def_type = unsafe { get_cme_def_type(cme) }; - if let VM_METHOD_TYPE_OPTIMIZED = def_type { - let opt_type = unsafe { get_cme_def_body_optimized_type(cme) }; - if let OPTIMIZED_METHOD_TYPE_SEND = opt_type { - gen_counter_incr!(asm, send_send_nested); - return CantCompile; - } - } - flags |= VM_CALL_FCALL | VM_CALL_OPT_SEND; assume_method_lookup_stable(jit, ocb, cme); |