summaryrefslogtreecommitdiff
path: root/test/ruby
diff options
context:
space:
mode:
authorStan Lo <stan.lo@shopify.com>2025-10-02 17:03:25 +0100
committerGitHub <noreply@github.com>2025-10-02 09:03:25 -0700
commit2ed5a02fcca4da4acf4c8c3d7ee4c392fc18d948 (patch)
tree834a2702d97afc92dcd2c905cace45cff46be5e2 /test/ruby
parent81f253577a77a934bfa02a33d80ca2a7c6af9a04 (diff)
ZJIT: Add `NoSingletonClass` patch point (#14680)
* ZJIT: Add NoSingletonClass patch point This patch point makes sure that when the object has a singleton class, the JIT code is invalidated. As of now, this is only needed for C call optimization. In YJIT, the singleton class guard only applies to Array, Hash, and String. But in ZJIT, we may optimize C calls from gems (e.g. `sqlite3`). So the patch point needs to be applied to a broader range of classes. * ZJIT: Only generate NoSingletonClass guard when the type can have singleton class * ZJIT: Update or forget NoSingletonClass patch point when needed
Diffstat (limited to 'test/ruby')
-rw-r--r--test/ruby/test_zjit.rb59
1 files changed, 59 insertions, 0 deletions
diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb
index 937cf44e19..83af7347d6 100644
--- a/test/ruby/test_zjit.rb
+++ b/test/ruby/test_zjit.rb
@@ -2912,6 +2912,65 @@ class TestZJIT < Test::Unit::TestCase
}, call_threshold: 2, insns: [:opt_new]
end
+ def test_singleton_class_invalidation_annotated_ccall
+ assert_compiles '[false, true]', %q{
+ def define_singleton(obj, define)
+ if define
+ # Wrap in C method frame to avoid exiting JIT on defineclass
+ [nil].reverse_each do
+ class << obj
+ def ==(_)
+ true
+ end
+ end
+ end
+ end
+ false
+ end
+
+ def test(define)
+ obj = BasicObject.new
+ # This == call gets compiled to a CCall
+ obj == define_singleton(obj, define)
+ end
+
+ result = []
+ result << test(false) # Compiles BasicObject#==
+ result << test(true) # Should use singleton#== now
+ result
+ }, call_threshold: 2
+ end
+
+ def test_singleton_class_invalidation_optimized_variadic_ccall
+ assert_compiles '[1, 1000]', %q{
+ def define_singleton(arr, define)
+ if define
+ # Wrap in C method frame to avoid exiting JIT on defineclass
+ [nil].reverse_each do
+ class << arr
+ def push(x)
+ super(x * 1000)
+ end
+ end
+ end
+ end
+ 1
+ end
+
+ def test(define)
+ arr = []
+ val = define_singleton(arr, define)
+ arr.push(val) # This CCall should be invalidated if singleton was defined
+ arr[0]
+ end
+
+ result = []
+ result << test(false) # Compiles Array#push as CCall
+ result << test(true) # Singleton defined, CCall should be invalidated
+ result
+ }, call_threshold: 2
+ end
+
private
# Assert that every method call in `test_script` can be compiled by ZJIT