diff options
author | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2016-07-28 11:02:30 +0000 |
---|---|---|
committer | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2016-07-28 11:02:30 +0000 |
commit | 9f60791a0412cae804f13ed3e746a634c7a0731b (patch) | |
tree | 49b3f9ff4924a622e13fbdfc9478d34717d19378 /proc.c | |
parent | 683eafd973e4db60756f6ee12e1b066e316f026f (diff) |
* vm_core.h: revisit the structure of frame, block and env.
[Bug #12628]
This patch introduce many changes.
* Introduce concept of "Block Handler (BH)" to represent
passed blocks.
* move rb_control_frame_t::flag to ep[0] (as a special local
variable). This flags represents not only frame type, but also
env flags such as escaped.
* rename `rb_block_t` to `struct rb_block`.
* Make Proc, Binding and RubyVM::Env objects wb-protected.
Check [Bug #12628] for more details.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55766 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'proc.c')
-rw-r--r-- | proc.c | 519 |
1 files changed, 319 insertions, 200 deletions
@@ -47,35 +47,51 @@ static int method_min_max_arity(VALUE, int *max); /* Proc */ #define IS_METHOD_PROC_IFUNC(ifunc) ((ifunc)->func == bmcall) -#define IS_METHOD_PROC_ISEQ(iseq) \ - (RUBY_VM_IFUNC_P(iseq) && \ - IS_METHOD_PROC_IFUNC((struct vm_ifunc *)(iseq))) + +static VALUE proc_to_s_(VALUE self, const rb_proc_t *proc); + +static void +block_mark(const struct rb_block *block) +{ + switch (vm_block_type(block)) { + case block_type_iseq: + case block_type_ifunc: + { + const struct rb_captured_block *captured = &block->as.captured; + RUBY_MARK_UNLESS_NULL(captured->self); + RUBY_MARK_UNLESS_NULL((VALUE)captured->code.val); + if (captured->ep && captured->ep[VM_ENV_DATA_INDEX_ENV] != Qundef /* cfunc_proc_t */) { + RUBY_MARK_UNLESS_NULL(VM_ENV_ENVVAL(captured->ep)); + } + } + break; + case block_type_symbol: + RUBY_MARK_UNLESS_NULL(block->as.symbol); + break; + case block_type_proc: + RUBY_MARK_UNLESS_NULL(block->as.proc); + break; + } +} static void proc_mark(void *ptr) { rb_proc_t *proc = ptr; - RUBY_MARK_UNLESS_NULL(proc->block.proc); - RUBY_MARK_UNLESS_NULL(proc->block.self); - if (proc->block.ep) { - RUBY_MARK_UNLESS_NULL(rb_vm_proc_envval(proc)); - } - if (proc->block.iseq && RUBY_VM_IFUNC_P(proc->block.iseq)) { - rb_gc_mark((VALUE)(proc->block.iseq)); - } + block_mark(&proc->block); RUBY_MARK_LEAVE("proc"); } typedef struct { rb_proc_t basic; - VALUE env[3]; /* me, specval, envval */ + VALUE env[VM_ENV_DATA_SIZE + 1]; /* ..., envval */ } cfunc_proc_t; static size_t proc_memsize(const void *ptr) { const rb_proc_t *proc = ptr; - if (proc->block.ep == ((const cfunc_proc_t *)ptr)->env+1) + if (proc->block.as.captured.ep == ((const cfunc_proc_t *)ptr)->env+1) return sizeof(cfunc_proc_t); return sizeof(rb_proc_t); } @@ -87,7 +103,7 @@ static const rb_data_type_t proc_data_type = { RUBY_TYPED_DEFAULT_FREE, proc_memsize, }, - 0, 0, RUBY_TYPED_FREE_IMMEDIATELY + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED }; VALUE @@ -108,21 +124,20 @@ rb_obj_is_proc(VALUE proc) } } +VALUE rb_proc_create(VALUE klass, const struct rb_block *block, + int8_t safe_level, int8_t is_from_method, int8_t is_lambda); + /* :nodoc: */ static VALUE proc_dup(VALUE self) { VALUE procval; rb_proc_t *src; - rb_proc_t *dst; GetProcPtr(self, src); - procval = rb_proc_alloc(rb_cProc); - GetProcPtr(procval, dst); - *dst = *src; - dst->block.proc = procval; + procval = rb_proc_create(rb_cProc, &src->block, + src->safe_level, src->is_from_method, src->is_lambda); RB_GC_GUARD(self); /* for: body = proc_dup(body) */ - return procval; } @@ -266,7 +281,8 @@ binding_mark(void *ptr) RUBY_MARK_ENTER("binding"); - RUBY_MARK_UNLESS_NULL(bind->env); + block_mark(&bind->block); + RUBY_MARK_UNLESS_NULL(bind->path); RUBY_MARK_LEAVE("binding"); @@ -305,7 +321,7 @@ binding_dup(VALUE self) rb_binding_t *src, *dst; GetBindingPtr(self, src); GetBindingPtr(bindval, dst); - dst->env = src->env; + dst->block = src->block; dst->path = src->path; dst->first_lineno = src->first_lineno; return bindval; @@ -375,7 +391,7 @@ bind_eval(int argc, VALUE *argv, VALUE bindval) return rb_f_eval(argc+1, args, Qnil /* self will be searched in eval */); } -static VALUE * +static const VALUE * get_local_variable_ptr(VALUE envval, ID lid) { rb_env_t *env; @@ -385,9 +401,9 @@ get_local_variable_ptr(VALUE envval, ID lid) unsigned int i; GetEnvPtr(envval, env); - iseq = env->block.iseq; + iseq = env->iseq; - if (RUBY_VM_NORMAL_ISEQ_P(iseq)) { + if (iseq && RUBY_VM_NORMAL_ISEQ_P(iseq)) { for (i=0; i<iseq->body->local_table_size; i++) { if (iseq->body->local_table[i] == lid) { return &env->env[i]; @@ -454,7 +470,7 @@ bind_local_variables(VALUE bindval) const rb_env_t *env; GetBindingPtr(bindval, bind); - GetEnvPtr(bind->env, env); + GetEnvPtr(VM_ENV_ENVVAL(vm_block_ep(&bind->block)), env); return rb_vm_env_local_variables(env); } @@ -487,7 +503,7 @@ bind_local_variable_get(VALUE bindval, VALUE sym) GetBindingPtr(bindval, bind); - if ((ptr = get_local_variable_ptr(bind->env, lid)) == NULL) { + if ((ptr = get_local_variable_ptr(VM_ENV_ENVVAL(vm_block_ep(&bind->block)), lid)) == NULL) { sym = ID2SYM(lid); undefined: rb_name_err_raise("local variable `%1$s' not defined for %2$s", @@ -526,17 +542,20 @@ bind_local_variable_set(VALUE bindval, VALUE sym, VALUE val) { ID lid = check_local_id(bindval, &sym); rb_binding_t *bind; - VALUE *ptr; + const VALUE *ptr; + VALUE envval; if (!lid) lid = rb_intern_str(sym); GetBindingPtr(bindval, bind); - if ((ptr = get_local_variable_ptr(bind->env, lid)) == NULL) { + envval = VM_ENV_ENVVAL(vm_block_ep(&bind->block)); + if ((ptr = get_local_variable_ptr(envval, lid)) == NULL) { /* not found. create new env */ ptr = rb_binding_add_dynavars(bind, 1, &lid); + envval = VM_ENV_ENVVAL(vm_block_ep(&bind->block)); } - *ptr = val; + RB_OBJ_WRITE(envval, ptr, val); return val; } @@ -567,7 +586,7 @@ bind_local_variable_defined_p(VALUE bindval, VALUE sym) if (!lid) return Qfalse; GetBindingPtr(bindval, bind); - return get_local_variable_ptr(bind->env, lid) ? Qtrue : Qfalse; + return get_local_variable_ptr(VM_ENV_ENVVAL(vm_block_ep(&bind->block)), lid) ? Qtrue : Qfalse; } /* @@ -580,11 +599,8 @@ static VALUE bind_receiver(VALUE bindval) { const rb_binding_t *bind; - const rb_env_t *env; - GetBindingPtr(bindval, bind); - GetEnvPtr(bind->env, env); - return env->block.self; + return vm_block_self(&bind->block); } static VALUE @@ -593,11 +609,19 @@ cfunc_proc_new(VALUE klass, VALUE ifunc, int8_t is_lambda) rb_proc_t *proc; cfunc_proc_t *sproc; VALUE procval = TypedData_Make_Struct(klass, cfunc_proc_t, &proc_data_type, sproc); - sproc->env[1] = VM_ENVVAL_BLOCK_PTR(0); + VALUE *ep; + proc = &sproc->basic; - proc->block.ep = sproc->env+1; - proc->block.iseq = (rb_iseq_t *)ifunc; - proc->block.proc = procval; + vm_block_type_set(&proc->block, block_type_ifunc); + + *(VALUE **)&proc->block.as.captured.ep = ep = sproc->env + VM_ENV_DATA_SIZE-1; + ep[VM_ENV_DATA_INDEX_FLAGS] = VM_FRAME_MAGIC_IFUNC | VM_ENV_FLAG_LOCAL | VM_ENV_FLAG_ESCAPED; + ep[VM_ENV_DATA_INDEX_ME_CREF] = Qfalse; + ep[VM_ENV_DATA_INDEX_SPECVAL] = VM_BLOCK_HANDLER_NONE; + ep[VM_ENV_DATA_INDEX_ENV] = Qundef; /* envval */ + + /* self? */ + RB_OBJ_WRITE(procval, &proc->block.as.captured.code.ifunc, ifunc); proc->is_lambda = is_lambda; return procval; } @@ -605,7 +629,13 @@ cfunc_proc_new(VALUE klass, VALUE ifunc, int8_t is_lambda) static VALUE sym_proc_new(VALUE klass, VALUE sym) { - return cfunc_proc_new(klass, sym, 0); + VALUE procval = rb_proc_alloc(klass); + rb_proc_t *proc; + GetProcPtr(procval, proc); + + vm_block_type_set(&proc->block, block_type_symbol); + RB_OBJ_WRITE(procval, &proc->block.as.symbol, sym); + return procval; } VALUE @@ -625,16 +655,23 @@ static const char proc_without_block[] = "tried to create Proc object without a static VALUE proc_new(VALUE klass, int8_t is_lambda) { - VALUE procval = Qnil; + VALUE procval; rb_thread_t *th = GET_THREAD(); rb_control_frame_t *cfp = th->cfp; - rb_block_t *block; + VALUE block_handler; - if (!(block = rb_vm_control_frame_block_ptr(cfp))) { + if ((block_handler = rb_vm_frame_block_handler(cfp)) == VM_BLOCK_HANDLER_NONE) { #if !PROC_NEW_REQUIRES_BLOCK cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); - if ((block = rb_vm_control_frame_block_ptr(cfp)) != 0) { + if ((block_handler = rb_vm_frame_block_handler(cfp)) != VM_BLOCK_HANDLER_NONE) { + const VALUE *lep = rb_vm_ep_local_ep(cfp->ep); + + if (VM_ENV_ESCAPED_P(lep)) { + procval = VM_ENV_PROCVAL(lep); + goto return_existing_proc; + } + if (is_lambda) { rb_warn(proc_without_block); } @@ -647,13 +684,13 @@ proc_new(VALUE klass, int8_t is_lambda) } } - procval = block->proc; + /* block is in cf */ + switch (vm_block_handler_type(block_handler)) { + case block_handler_type_proc: + procval = VM_BH_TO_PROC(block_handler); - if (procval) { - if (SYMBOL_P(procval)) { - return (klass != rb_cProc) ? sym_proc_new(klass, procval) : rb_sym_to_proc(procval); - } - else if (RBASIC_CLASS(procval) == klass) { + return_existing_proc: + if (RBASIC_CLASS(procval) == klass) { return procval; } else { @@ -661,10 +698,20 @@ proc_new(VALUE klass, int8_t is_lambda) RBASIC_SET_CLASS(newprocval, klass); return newprocval; } - } + break; - procval = rb_vm_make_proc_lambda(th, block, klass, is_lambda); - return procval; + case block_handler_type_symbol: + return (klass != rb_cProc) ? + sym_proc_new(klass, VM_BH_TO_SYMBOL(block_handler)) : + rb_sym_to_proc(VM_BH_TO_SYMBOL(block_handler)); + break; + + case block_handler_type_ifunc: + case block_handler_type_iseq: + return rb_vm_make_proc_lambda(th, VM_BH_TO_CAPT_BLOCK(block_handler), klass, is_lambda); + } + VM_UNREACHABLE(proc_new); + return Qnil; } /* @@ -777,27 +824,7 @@ rb_block_lambda(void) static VALUE proc_call(int argc, VALUE *argv, VALUE procval) { - VALUE vret; - const rb_block_t *blockptr = 0; - const rb_iseq_t *iseq; - rb_proc_t *proc; - VALUE passed_procval; - GetProcPtr(procval, proc); - - iseq = proc->block.iseq; - if (RUBY_VM_IFUNC_P(iseq) || iseq->body->param.flags.has_block) { - if (rb_block_given_p()) { - rb_proc_t *passed_proc; - passed_procval = rb_block_proc(); - GetProcPtr(passed_procval, passed_proc); - blockptr = &passed_proc->block; - } - } - - vret = rb_vm_invoke_proc(GET_THREAD(), proc, argc, argv, blockptr); - RB_GC_GUARD(procval); - RB_GC_GUARD(passed_procval); - return vret; + /* removed */ } #endif @@ -815,44 +842,35 @@ check_argc(long argc) #define check_argc(argc) (argc) #endif -static rb_block_t * -passed_block(VALUE pass_procval) -{ - if (!NIL_P(pass_procval)) { - rb_proc_t *pass_proc; - if (SYMBOL_P(pass_procval)) { - pass_procval = sym_proc_new(rb_cProc, pass_procval); - } - GetProcPtr(pass_procval, pass_proc); - return &pass_proc->block; - } - return 0; -} - VALUE rb_proc_call(VALUE self, VALUE args) { VALUE vret; rb_proc_t *proc; GetProcPtr(self, proc); - vret = rb_vm_invoke_proc(GET_THREAD(), proc, check_argc(RARRAY_LEN(args)), RARRAY_CONST_PTR(args), 0); + vret = rb_vm_invoke_proc(GET_THREAD(), proc, + check_argc(RARRAY_LEN(args)), RARRAY_CONST_PTR(args), + VM_BLOCK_HANDLER_NONE); RB_GC_GUARD(self); RB_GC_GUARD(args); return vret; } +static VALUE +proc_to_block_handler(VALUE procval) +{ + return NIL_P(procval) ? VM_BLOCK_HANDLER_NONE : procval; +} + VALUE -rb_proc_call_with_block(VALUE self, int argc, const VALUE *argv, VALUE pass_procval) +rb_proc_call_with_block(VALUE self, int argc, const VALUE *argv, VALUE passed_procval) { + rb_thread_t *th = GET_THREAD(); VALUE vret; rb_proc_t *proc; - rb_block_t *block = 0; GetProcPtr(self, proc); - - block = passed_block(pass_procval); - vret = rb_vm_invoke_proc(GET_THREAD(), proc, argc, argv, block); + vret = rb_vm_invoke_proc(th, proc, argc, argv, proc_to_block_handler(passed_procval)); RB_GC_GUARD(self); - RB_GC_GUARD(pass_procval); return vret; } @@ -916,21 +934,24 @@ rb_iseq_min_max_arity(const rb_iseq_t *iseq, int *max) } static int -rb_block_min_max_arity(rb_block_t *block, int *max) -{ - const rb_iseq_t *iseq = block->iseq; - - if (iseq) { - if (RUBY_VM_NORMAL_ISEQ_P(iseq)) { - return rb_iseq_min_max_arity(iseq, max); - } - else { - if (IS_METHOD_PROC_ISEQ(iseq)) { - const struct vm_ifunc *ifunc = (struct vm_ifunc *)iseq; +rb_block_min_max_arity(const struct rb_block *block, int *max) +{ + switch (vm_block_type(block)) { + case block_type_iseq: + return rb_iseq_min_max_arity(block->as.captured.code.iseq, max); + case block_type_proc: + return rb_block_min_max_arity(vm_proc_block(block->as.proc), max); + case block_type_ifunc: + { + const struct vm_ifunc *ifunc = block->as.captured.code.ifunc; + if (IS_METHOD_PROC_IFUNC(ifunc)) { /* e.g. method(:foo).to_proc.arity */ return method_min_max_arity((VALUE)ifunc->data, max); } } + /* fall through */ + case block_type_symbol: + break; } *max = UNLIMITED_ARGUMENTS; return 0; @@ -946,7 +967,7 @@ static int rb_proc_min_max_arity(VALUE self, int *max) { rb_proc_t *proc; - rb_block_t *block; + const struct rb_block *block; GetProcPtr(self, proc); block = &proc->block; return rb_block_min_max_arity(block, max); @@ -961,57 +982,95 @@ rb_proc_arity(VALUE self) return (proc->is_lambda ? min == max : max != UNLIMITED_ARGUMENTS) ? min : -min-1; } +static void +block_setup(struct rb_block *block, VALUE block_handler) +{ + switch (vm_block_handler_type(block_handler)) { + case block_handler_type_iseq: + block->type = block_type_iseq; + block->as.captured = *VM_BH_TO_ISEQ_BLOCK(block_handler); + break; + case block_handler_type_ifunc: + block->type = block_type_ifunc; + block->as.captured = *VM_BH_TO_IFUNC_BLOCK(block_handler); + break; + case block_handler_type_symbol: + block->type = block_type_symbol; + block->as.symbol = VM_BH_TO_SYMBOL(block_handler); + break; + case block_handler_type_proc: + block->type = block_type_proc; + block->as.proc = VM_BH_TO_PROC(block_handler); + } +} + int rb_block_arity(void) { int min, max; rb_thread_t *th = GET_THREAD(); rb_control_frame_t *cfp = th->cfp; - rb_block_t *block = rb_vm_control_frame_block_ptr(cfp); - VALUE proc_value; - - if (!block) rb_raise(rb_eArgError, "no block given"); - min = rb_block_min_max_arity(block, &max); - proc_value = block->proc; - if (proc_value) { - if (SYMBOL_P(proc_value)) { - return -1; - } - else { + VALUE block_handler = rb_vm_frame_block_handler(cfp); + struct rb_block block; + + if (block_handler == VM_BLOCK_HANDLER_NONE) { + rb_raise(rb_eArgError, "no block given"); + } + + block_setup(&block, block_handler); + min = rb_block_min_max_arity(&block, &max); + + switch (vm_block_type(&block)) { + case block_handler_type_symbol: + return -1; + + case block_handler_type_proc: + { + VALUE procval = block_handler; rb_proc_t *proc; - GetProcPtr(proc_value, proc); - if (proc) - return (proc->is_lambda ? min == max : max != UNLIMITED_ARGUMENTS) ? min : -min-1; + GetProcPtr(procval, proc); + return (proc->is_lambda ? min == max : max != UNLIMITED_ARGUMENTS) ? min : -min-1; + /* fall through */ } + + default: + return max != UNLIMITED_ARGUMENTS ? min : -min-1; } - return max != UNLIMITED_ARGUMENTS ? min : -min-1; } const rb_iseq_t * rb_proc_get_iseq(VALUE self, int *is_proc) { const rb_proc_t *proc; - const rb_iseq_t *iseq; + const struct rb_block *block; GetProcPtr(self, proc); - iseq = proc->block.iseq; + block = &proc->block; if (is_proc) *is_proc = !proc->is_lambda; - if (RUBY_VM_IFUNC_P(iseq)) { - const struct vm_ifunc *ifunc = (struct vm_ifunc *)iseq; - iseq = 0; - if (IS_METHOD_PROC_IFUNC(ifunc)) { - /* method(:foo).to_proc */ - iseq = rb_method_iseq((VALUE)ifunc->data); - if (is_proc) *is_proc = 0; + + switch (vm_block_type(block)) { + case block_type_iseq: + return rb_iseq_check(block->as.captured.code.iseq); + case block_type_proc: + return rb_proc_get_iseq(block->as.proc, is_proc); + case block_type_ifunc: + { + const struct vm_ifunc *ifunc = block->as.captured.code.ifunc; + if (IS_METHOD_PROC_IFUNC(ifunc)) { + /* method(:foo).to_proc */ + if (is_proc) *is_proc = 0; + return rb_method_iseq((VALUE)ifunc->data); + } + else { + return NULL; + } } - return iseq; - } - else if (SYMBOL_P(iseq)) { + case block_type_symbol: return NULL; } - else { - return rb_iseq_check(iseq); - } + + VM_UNREACHABLE(rb_proc_get_iseq); + return NULL; } static VALUE @@ -1090,8 +1149,9 @@ rb_hash_proc(st_index_t hash, VALUE prc) { rb_proc_t *proc; GetProcPtr(prc, proc); - hash = rb_hash_uint(hash, (st_index_t)proc->block.iseq); - return rb_hash_uint(hash, (st_index_t)proc->block.ep >> 16); + hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.code.val); + hash = rb_hash_uint(hash, (st_index_t)proc->block.as.captured.self); + return rb_hash_uint(hash, (st_index_t)proc->block.as.captured.ep >> 16); } VALUE @@ -1153,34 +1213,40 @@ proc_hash(VALUE self) */ static VALUE -proc_to_s(VALUE self) +proc_to_s_(VALUE self, const rb_proc_t *proc) { VALUE str = 0; - rb_proc_t *proc; const char *cname = rb_obj_classname(self); - const rb_iseq_t *iseq; + const struct rb_block *block; const char *is_lambda; - GetProcPtr(self, proc); - iseq = proc->block.iseq; + block = &proc->block; is_lambda = proc->is_lambda ? " (lambda)" : ""; - if (RUBY_VM_NORMAL_ISEQ_P(iseq) && rb_iseq_check(iseq)) { - int first_lineno = 0; - - if (iseq->body->line_info_table) { - first_lineno = FIX2INT(rb_iseq_first_lineno(iseq)); + again: + switch (vm_block_type(block)) { + case block_type_proc: + block = vm_proc_block(block->as.proc); + goto again; + case block_type_iseq: + { + const rb_iseq_t *iseq = rb_iseq_check(block->as.captured.code.iseq); + int first_lineno = 0; + if (iseq->body->line_info_table) { + first_lineno = FIX2INT(rb_iseq_first_lineno(iseq)); + } + str = rb_sprintf("#<%s:%p@%"PRIsVALUE":%d%s>", cname, (void *)self, + iseq->body->location.path, first_lineno, is_lambda); } - str = rb_sprintf("#<%s:%p@%"PRIsVALUE":%d%s>", cname, (void *)self, - iseq->body->location.path, first_lineno, is_lambda); - } - else if (SYMBOL_P(iseq)) { + break; + case block_type_symbol: str = rb_sprintf("#<%s:%p(&%+"PRIsVALUE")%s>", cname, (void *)self, - (VALUE)iseq, is_lambda); - } - else { - str = rb_sprintf("#<%s:%p%s>", cname, (void *)proc->block.iseq, + block->as.symbol, is_lambda); + break; + case block_type_ifunc: + str = rb_sprintf("#<%s:%p%s>", cname, proc->block.as.captured.code.ifunc, is_lambda); + break; } if (OBJ_TAINTED(self)) { @@ -1189,6 +1255,14 @@ proc_to_s(VALUE self) return str; } +static VALUE +proc_to_s(VALUE self) +{ + const rb_proc_t *proc; + GetProcPtr(self, proc); + return proc_to_s_(self, proc); +} + /* * call-seq: * prc.to_proc -> proc @@ -1791,16 +1865,19 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod) body = rb_block_lambda(); #else rb_thread_t *th = GET_THREAD(); - rb_block_t *block = rb_vm_control_frame_block_ptr(th->cfp); - if (!block) rb_raise(rb_eArgError, proc_without_block); + VALUE block_handler = rb_vm_frame_block_handler(th->cfp); + if (block_handler == VM_BLOCK_HANDLER_NONE) rb_raise(rb_eArgError, proc_without_block); - body = block->proc; - - if (SYMBOL_P(body)) { - body = rb_sym_to_proc(body); - } - else if (!body) { - body = rb_vm_make_proc_lambda(th, block, rb_cProc, TRUE); + switch (vm_block_handler_type(block_handler)) { + case block_handler_type_proc: + body = VM_BH_TO_PROC(block_handler); + break; + case block_handler_type_symbol: + body = rb_sym_to_proc(VM_BH_TO_SYMBOL(block_handler)); + break; + case block_handler_type_iseq: + case block_handler_type_ifunc: + body = rb_vm_make_proc_lambda(th, VM_BH_TO_CAPT_BLOCK(block_handler), rb_cProc, TRUE); } #endif } @@ -1842,14 +1919,14 @@ rb_mod_define_method(int argc, VALUE *argv, VALUE mod) RB_GC_GUARD(body); } else { - rb_proc_t *proc; - body = proc_dup(body); - GetProcPtr(body, proc); - if (RUBY_VM_NORMAL_ISEQ_P(proc->block.iseq)) { + VALUE procval = proc_dup(body); + if (vm_proc_iseq(procval) != NULL) { + rb_proc_t *proc; + GetProcPtr(procval, proc); proc->is_lambda = TRUE; proc->is_from_method = TRUE; } - rb_add_method(mod, id, VM_METHOD_TYPE_BMETHOD, (void *)body, scope_visi->method_visi); + rb_add_method(mod, id, VM_METHOD_TYPE_BMETHOD, (void *)procval, scope_visi->method_visi); if (scope_visi->module_func) { rb_add_method(rb_singleton_class(mod), id, VM_METHOD_TYPE_BMETHOD, (void *)body, METHOD_VISI_PUBLIC); } @@ -1963,8 +2040,8 @@ method_clone(VALUE self) VALUE rb_method_call(int argc, const VALUE *argv, VALUE method) { - VALUE proc = rb_block_given_p() ? rb_block_proc() : Qnil; - return rb_method_call_with_block(argc, argv, method, proc); + VALUE procval = rb_block_given_p() ? rb_block_proc() : Qnil; + return rb_method_call_with_block(argc, argv, method, procval); } static const rb_callable_method_entry_t * @@ -1976,16 +2053,16 @@ method_callable_method_entry(const struct METHOD *data) static inline VALUE call_method_data(rb_thread_t *th, const struct METHOD *data, - int argc, const VALUE *argv, VALUE pass_procval) + int argc, const VALUE *argv, VALUE passed_procval) { - th->passed_block = passed_block(pass_procval); + vm_passed_block_handler_set(th, proc_to_block_handler(passed_procval)); return rb_vm_call(th, data->recv, data->me->called_id, argc, argv, method_callable_method_entry(data)); } static VALUE call_method_data_safe(rb_thread_t *th, const struct METHOD *data, - int argc, const VALUE *argv, VALUE pass_procval, + int argc, const VALUE *argv, VALUE passed_procval, int safe) { VALUE result = Qnil; /* OK */ @@ -1995,7 +2072,7 @@ call_method_data_safe(rb_thread_t *th, const struct METHOD *data, if ((state = TH_EXEC_TAG()) == 0) { /* result is used only if state == 0, no exceptions is caught. */ /* otherwise it doesn't matter even if clobbered. */ - NO_CLOBBERED(result) = call_method_data(th, data, argc, argv, pass_procval); + NO_CLOBBERED(result) = call_method_data(th, data, argc, argv, passed_procval); } TH_POP_TAG(); rb_set_safe_level_force(safe); @@ -2005,7 +2082,7 @@ call_method_data_safe(rb_thread_t *th, const struct METHOD *data, } VALUE -rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE pass_procval) +rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE passed_procval) { const struct METHOD *data; rb_thread_t *const th = GET_THREAD(); @@ -2019,10 +2096,10 @@ rb_method_call_with_block(int argc, const VALUE *argv, VALUE method, VALUE pass_ int safe = rb_safe_level(); if (safe < safe_level_to_run) { rb_set_safe_level_force(safe_level_to_run); - return call_method_data_safe(th, data, argc, argv, pass_procval, safe); + return call_method_data_safe(th, data, argc, argv, passed_procval, safe); } } - return call_method_data(th, data, argc, argv, pass_procval); + return call_method_data(th, data, argc, argv, passed_procval); } /********************************************************************** @@ -2627,8 +2704,18 @@ localjump_reason(VALUE exc) rb_cref_t *rb_vm_cref_new_toplevel(void); /* vm.c */ +static inline void +env_write(VALUE env, const VALUE *ep, int index, VALUE v) +{ + VM_ASSERT(VM_ENV_ESCAPED_P(ep)); + VM_ASSERT(env == VM_ENV_ENVVAL(ep)); + VM_ASSERT(vm_env_ep(env) == ep); + + RB_OBJ_WRITE(env, &ep[index], v); +} + static VALUE -env_clone(VALUE envval, VALUE receiver, const rb_cref_t *cref) +env_clone(VALUE envval, const rb_cref_t *cref) { VALUE newenvval = TypedData_Wrap_Struct(RBASIC_CLASS(envval), RTYPEDDATA_TYPE(envval), 0); rb_env_t *env, *newenv; @@ -2642,9 +2729,11 @@ env_clone(VALUE envval, VALUE receiver, const rb_cref_t *cref) envsize = sizeof(rb_env_t) + (env->env_size - 1) * sizeof(VALUE); newenv = xmalloc(envsize); memcpy(newenv, env, envsize); + VM_ASSERT(env->ep > env->env); + newenv->ep = &newenv->env[env->ep - env->env]; + VM_FORCE_WRITE(&newenv->ep[VM_ENV_DATA_INDEX_ENV], newenvval); RTYPEDDATA_DATA(newenvval) = newenv; - newenv->block.self = receiver; - newenv->block.ep[-1] = (VALUE)cref; + env_write(newenvval, newenv->ep, VM_ENV_DATA_INDEX_ME_CREF, (VALUE)cref); return newenvval; } @@ -2666,31 +2755,61 @@ env_clone(VALUE envval, VALUE receiver, const rb_cref_t *cref) static VALUE proc_binding(VALUE self) { - VALUE bindval, envval; - const rb_proc_t *proc; - const rb_iseq_t *iseq; + VALUE bindval, envval = Qundef, binding_self = Qundef; rb_binding_t *bind; + const rb_proc_t *proc; + const rb_iseq_t *iseq = NULL; + const struct rb_block *block; + const rb_env_t *env; GetProcPtr(self, proc); - envval = rb_vm_proc_envval(proc); - iseq = proc->block.iseq; - if (SYMBOL_P(iseq)) goto error; - if (RUBY_VM_IFUNC_P(iseq)) { - struct vm_ifunc *ifunc = (struct vm_ifunc *)iseq; - if (IS_METHOD_PROC_IFUNC(ifunc)) { - VALUE method = (VALUE)ifunc->data; - envval = env_clone(envval, method_receiver(method), method_cref(method)); - iseq = rb_method_iseq(method); - } - else { - error: - rb_raise(rb_eArgError, "Can't create Binding from C level Proc"); + block = &proc->block; + + again: + switch (vm_block_type(block)) { + case block_type_iseq: + iseq = block->as.captured.code.iseq; + binding_self = block->as.captured.self; + envval = VM_ENV_ENVVAL(block->as.captured.ep); + break; + case block_type_proc: + GetProcPtr(block->as.proc, proc); + block = &proc->block; + goto again; + case block_type_symbol: + goto error; + case block_type_ifunc: + { + const struct vm_ifunc *ifunc = block->as.captured.code.ifunc; + if (IS_METHOD_PROC_IFUNC(ifunc)) { + VALUE method = (VALUE)ifunc->data; + rb_env_t *newenv; + + iseq = rb_method_iseq(method); + envval = VM_ENV_ENVVAL(block->as.captured.ep); + envval = env_clone(envval, method_cref(method)); + binding_self = method_receiver(method); + + GetEnvPtr(envval, newenv); + /* set empty iseq */ + newenv->iseq = rb_iseq_new(NULL, rb_str_new2("<empty iseq>"), rb_str_new2("<empty_iseq>"), Qnil, 0, ISEQ_TYPE_TOP); + break; + } + else { + error: + rb_raise(rb_eArgError, "Can't create Binding from C level Proc"); + return Qnil; + } } } bindval = rb_binding_alloc(rb_cBinding); GetBindingPtr(bindval, bind); - bind->env = envval; + GetEnvPtr(envval, env); + + bind->block.as.captured.self = binding_self; + bind->block.as.captured.code.iseq = env->iseq; + bind->block.as.captured.ep = env->ep; if (iseq) { rb_iseq_check(iseq); |