summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/ruby/test_yjit.rb41
1 files changed, 41 insertions, 0 deletions
diff --git a/test/ruby/test_yjit.rb b/test/ruby/test_yjit.rb
index 3ab04f6fe9..7f0f691320 100644
--- a/test/ruby/test_yjit.rb
+++ b/test/ruby/test_yjit.rb
@@ -1155,6 +1155,47 @@ class TestYJIT < Test::Unit::TestCase
RUBY
end
+ def test_return_to_invalidated_block
+ # [Bug #19463]
+ assert_compiles(<<~RUBY, result: [1, 1, :ugokanai])
+ klass = Class.new do
+ def self.lookup(hash, key) = hash[key]
+
+ def self.foo(a, b) = []
+
+ def self.test(hash, key)
+ [lookup(hash, key), key, "".freeze]
+ # 05 opt_send_without_block :lookup
+ # 07 getlocal_WC_0 :hash
+ # 09 opt_str_freeze ""
+ # 12 newarray 3
+ # 14 leave
+ #
+ # YJIT will put instructions (07..14) into a block.
+ # When String#freeze is redefined from within lookup(),
+ # the return address to the block is still on-stack. We rely
+ # on invalidation patching the code at the return address
+ # to service this situation correctly.
+ end
+ end
+
+ # get YJIT to compile test()
+ hash = { 1 => [] }
+ 31.times { klass.test(hash, 1) }
+
+ # inject invalidation into lookup()
+ evil_hash = Hash.new do |_, key|
+ class String
+ undef :freeze
+ def freeze = :ugokanai
+ end
+
+ key
+ end
+ klass.test(evil_hash, 1)
+ RUBY
+ end
+
private
def code_gc_helpers