* use correct svar

Without this patch, svar location is used "nearest Ruby frame".
It is almost correct but it doesn't correct when the `each` method
is written in Ruby.

```ruby
class C
  include Enumerable
  def each
    %w(bar baz).each{|e| yield e}
  end
end

C.new.grep(/(b.)/){|e| p [$1, e]}
```

This patch fix this issue by traversing ifunc's cfp.

Note that if cfp doesn't specify this Thread's cfp stack, reserved
svar location (`ec->root_svar`) is used.

* make yjit-bindgen

---------

Co-authored-by: Takashi Kokubun <takashikkbn@gmail.com>
This commit is contained in:
Koichi Sasada 2023-02-02 09:13:19 +09:00 коммит произвёл GitHub
Родитель 2675f2c864
Коммит 0a82bfe5e1
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 21 добавлений и 8 удалений

Просмотреть файл

@ -82,7 +82,7 @@ struct vm_ifunc_argc {
/*! IFUNC (Internal FUNCtion) */ /*! IFUNC (Internal FUNCtion) */
struct vm_ifunc { struct vm_ifunc {
VALUE flags; VALUE flags;
VALUE reserved; struct rb_control_frame_struct *owner_cfp;
rb_block_call_func_t func; rb_block_call_func_t func;
const void *data; const void *data;
struct vm_ifunc_argc argc; struct vm_ifunc_argc argc;

2
proc.c
Просмотреть файл

@ -752,7 +752,7 @@ rb_vm_ifunc_new(rb_block_call_func_t func, const void *data, int min_argc, int m
} }
arity.argc.min = min_argc; arity.argc.min = min_argc;
arity.argc.max = max_argc; arity.argc.max = max_argc;
VALUE ret = rb_imemo_new(imemo_ifunc, (VALUE)func, (VALUE)data, arity.packed, 0); VALUE ret = rb_imemo_new(imemo_ifunc, (VALUE)func, (VALUE)data, arity.packed, (VALUE)GET_EC()->cfp);
return (struct vm_ifunc *)ret; return (struct vm_ifunc *)ret;
} }

23
vm.c
Просмотреть файл

@ -1621,12 +1621,25 @@ rb_vm_invoke_proc_with_self(rb_execution_context_t *ec, rb_proc_t *proc, VALUE s
/* special variable */ /* special variable */
static rb_control_frame_t * static rb_control_frame_t *
vm_normal_frame(const rb_execution_context_t *ec, rb_control_frame_t *cfp) vm_svar_frame(const rb_execution_context_t *ec, rb_control_frame_t *cfp)
{ {
while (cfp->pc == 0) { while (cfp->pc == 0) {
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_IFUNC) {
struct vm_ifunc *ifunc = (struct vm_ifunc *)cfp->iseq;
rb_control_frame_t *owner_cfp = ifunc->owner_cfp;
if (cfp < owner_cfp) {
cfp = ifunc->owner_cfp;
}
else {
return NULL;
}
}
else {
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
}
if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) { if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)) {
return 0; return NULL;
} }
} }
return cfp; return cfp;
@ -1635,14 +1648,14 @@ vm_normal_frame(const rb_execution_context_t *ec, rb_control_frame_t *cfp)
static VALUE static VALUE
vm_cfp_svar_get(const rb_execution_context_t *ec, rb_control_frame_t *cfp, VALUE key) vm_cfp_svar_get(const rb_execution_context_t *ec, rb_control_frame_t *cfp, VALUE key)
{ {
cfp = vm_normal_frame(ec, cfp); cfp = vm_svar_frame(ec, cfp);
return lep_svar_get(ec, cfp ? VM_CF_LEP(cfp) : 0, key); return lep_svar_get(ec, cfp ? VM_CF_LEP(cfp) : 0, key);
} }
static void static void
vm_cfp_svar_set(const rb_execution_context_t *ec, rb_control_frame_t *cfp, VALUE key, const VALUE val) vm_cfp_svar_set(const rb_execution_context_t *ec, rb_control_frame_t *cfp, VALUE key, const VALUE val)
{ {
cfp = vm_normal_frame(ec, cfp); cfp = vm_svar_frame(ec, cfp);
lep_svar_set(ec, cfp ? VM_CF_LEP(cfp) : 0, key, val); lep_svar_set(ec, cfp ? VM_CF_LEP(cfp) : 0, key, val);
} }

Просмотреть файл

@ -570,7 +570,7 @@ pub struct vm_ifunc_argc {
#[repr(C)] #[repr(C)]
pub struct vm_ifunc { pub struct vm_ifunc {
pub flags: VALUE, pub flags: VALUE,
pub reserved: VALUE, pub owner_cfp: *mut rb_control_frame_struct,
pub func: rb_block_call_func_t, pub func: rb_block_call_func_t,
pub data: *const ::std::os::raw::c_void, pub data: *const ::std::os::raw::c_void,
pub argc: vm_ifunc_argc, pub argc: vm_ifunc_argc,