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_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?");
|
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
|
// Same as rb_vm_frame_block_handler
|
||||||
let ep_opnd = gen_get_lep(jit, asm);
|
let ep_opnd = gen_get_lep(jit, asm);
|
||||||
let block_handler = asm.load(
|
let block_handler = asm.load(
|
||||||
|
@ -6666,6 +6671,9 @@ fn gen_block_given(
|
||||||
asm.cmp(block_handler, VM_BLOCK_HANDLER_NONE.into());
|
asm.cmp(block_handler, VM_BLOCK_HANDLER_NONE.into());
|
||||||
let block_given = asm.csel_ne(true_opnd, false_opnd);
|
let block_given = asm.csel_ne(true_opnd, false_opnd);
|
||||||
asm.mov(out_opnd, block_given);
|
asm.mov(out_opnd, block_given);
|
||||||
|
} else {
|
||||||
|
asm.mov(out_opnd, false_opnd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Codegen for rb_class_superclass()
|
// Codegen for rb_class_superclass()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue