diff options
| author | Alan Wu <XrXr@users.noreply.github.com> | 2026-02-09 16:46:50 -0500 |
|---|---|---|
| committer | Alan Wu <XrXr@users.noreply.github.com> | 2026-02-18 10:16:43 -0500 |
| commit | ea7d3eb89b8c5192c0cf9a885a17b0aeaef1067a (patch) | |
| tree | c669740a109e20a257ec3b834be30777b6b1e329 /bootstraptest | |
| parent | d256629bf9e194838d1837be74dcc0b8ff0bcfd6 (diff) | |
YJIT: Fix always-failing guard for `super()` in BMETHODs
Previously, when dealing with a `super()` nested in a block that runs as
a method (through e.g. `define_method`), YJIT generated a guard that
never passes leading to a misidentification of the callsite as
megamorphic and an unconditional interpreter fallback.
The issue was in the subroutine to find the currently running method
entry. In the interpreter, this is rb_vm_frame_method_entry(). YJIT used
`gen_get_lep()` to find the EP with `VM_ENV_FLAG_LOCAL`, but in case
of BMETHODs, the corresponding CME is never at an EP level with
`VM_ENV_FLAG_LOCAL` set.
Because each block nesting level can dynamically run as either a BMETHOD
or not, starting at a block and finding the first EP that has a method
entry ultimately requires a search loop such as the one in rb_vm_frame_method_entry().
This patch introduces such a loop.
Because `invokesuper` in a block can now work end-to-end, add check for the
previously masked "implicit argument passing of super from method
defined by define_method() is not supported..." condition.
Diffstat (limited to 'bootstraptest')
| -rw-r--r-- | bootstraptest/test_yjit.rb | 26 |
1 files changed, 26 insertions, 0 deletions
diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index be66395190..cc7d9f1aeb 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -2483,6 +2483,32 @@ assert_equal '[0, 2]', %q{ B.new.foo } +# invokesuper in a weird block +assert_equal '["block->A#itself", "block->singleton#itself"]', %q{ + # This test runs the same block as first as a block and then as a method, + # testing the routine that finds the currently running method, which is + # relevant for `super`. + class BlockIseqDuality + prepend(Module.new do + def itself + nested = -> { "block->" + super() } + @singleton_itself.define_singleton_method(:itself, &nested) + nested + end + end) + + attr_reader :singleton_itself + def initialize = (@singleton_itself = "singleton#itself") + + def itself = "A#itself" + end + + tester = BlockIseqDuality.new + super_lambda = tester.itself + super_lambda.call # warmup + [super_lambda.call, tester.singleton_itself.itself] +} + # invokesuper zsuper in a bmethod assert_equal 'ok', %q{ class Foo |
