mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
YJIT: Fix defined?(yield)
and block_given?
at top level
Previously, YJIT returned truthy for the block given query at the top level. That's incorrect because the top level script never receives a block, and `yield` is a syntax error there. Inside methods, the number of hops to get from `iseq` to `iseq->body->local_iseq` is the same as the number of `VM_ENV_PREV_EP(ep)` hops to get to an environment with `VM_ENV_FLAG_LOCAL`. YJIT and the interpreter both rely on this as can be seen in get_lvar_level(). However, this identity does not hold for the top level frame because of vm_set_eval_stack(), which sets up `TOPLEVEL_BINDING`. Since only methods can take a block that `yield` goes to, have ISEQs that are the child of a non-method ISEQ return falsy for the block given query. This fixes the issue for the top level script and is an optimization for non-method contexts such as inside `ISEQ_TYPE_CLASS`.
This commit is contained in:
parent
b080fcd3cd
commit
38558dd95e
2 changed files with 44 additions and 9 deletions
|
@ -5342,3 +5342,30 @@ assert_equal 'nil', %{
|
|||
|
||||
test_local_fill_in_forwardable.inspect
|
||||
}
|
||||
|
||||
# Test defined?(yield) and block_given? in non-method context.
|
||||
# It's good that the body of this runs at true top level and isn't wrapped in a block.
|
||||
assert_equal 'false', %{
|
||||
RESULT = []
|
||||
RESULT << defined?(yield)
|
||||
RESULT << block_given?
|
||||
|
||||
1.times do
|
||||
RESULT << defined?(yield)
|
||||
RESULT << block_given?
|
||||
end
|
||||
|
||||
module ModuleContext
|
||||
1.times do
|
||||
RESULT << defined?(yield)
|
||||
RESULT << block_given?
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
RESULT << defined?(yield)
|
||||
RESULT << block_given?
|
||||
end
|
||||
|
||||
RESULT.any?
|
||||
}
|
||||
|
|
|
@ -6656,6 +6656,11 @@ fn gen_block_given(
|
|||
) {
|
||||
asm_comment!(asm, "block_given?");
|
||||
|
||||
// `yield` goes to the block handler stowed in the "local" iseq which is
|
||||
// the current iseq or a parent. Only the "method" iseq type can be passed a
|
||||
// block handler. (e.g. `yield` in the top level script is a syntax error.)
|
||||
let local_iseq = unsafe { rb_get_iseq_body_local_iseq(jit.iseq) };
|
||||
if unsafe { rb_get_iseq_body_type(local_iseq) } == ISEQ_TYPE_METHOD {
|
||||
// Same as rb_vm_frame_block_handler
|
||||
let ep_opnd = gen_get_lep(jit, asm);
|
||||
let block_handler = asm.load(
|
||||
|
@ -6666,6 +6671,9 @@ fn gen_block_given(
|
|||
asm.cmp(block_handler, VM_BLOCK_HANDLER_NONE.into());
|
||||
let block_given = asm.csel_ne(true_opnd, false_opnd);
|
||||
asm.mov(out_opnd, block_given);
|
||||
} else {
|
||||
asm.mov(out_opnd, false_opnd);
|
||||
}
|
||||
}
|
||||
|
||||
// Codegen for rb_class_superclass()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue