From: "mame (Yusuke Endoh)" Date: 2012-04-17T22:53:04+09:00 Subject: [ruby-core:44422] [ruby-trunk - Feature #5007] Proc#call_under: Unifying instance_eval and instance_exec Issue #5007 has been updated by mame (Yusuke Endoh). File proc_call_under.patch added Hello, I made a proof-of-concept patch. Not tested yet. Please try it and find a bug. It (and some related functions) seem to need some refactoring work because it calls directly invoke_block_from_c which is very internal function. $ ./miniruby -e ' p proc { self }.call_under(1) p proc { |a| self + a }.call_under(1, 2) p proc { |&b| self + b.call }.call_under(2) { 2 } ' 1 3 4 diff --git a/proc.c b/proc.c index d44e8d8..7ad490e 100644 --- a/proc.c +++ b/proc.c @@ -567,6 +567,22 @@ proc_call(int argc, VALUE *argv, VALUE procval) return vret; } +VALUE rb_proc_call_under(VALUE procval, VALUE under, VALUE self, VALUE values); + +static VALUE +proc_call_under(int argc, VALUE *argv, VALUE procval) +{ + VALUE self, klass, values; + rb_scan_args(argc, argv, "1*", &self, &values); + if (SPECIAL_CONST_P(self)) { + klass = Qnil; + } + else { + klass = rb_singleton_class(self); + } + return rb_proc_call_under(procval, klass, self, values); +} + #if SIZEOF_LONG > SIZEOF_INT static inline int check_argc(long argc) @@ -2183,6 +2199,7 @@ Init_Proc(void) rb_define_method(rb_cProc, "[]", proc_call, -1); rb_define_method(rb_cProc, "===", proc_call, -1); rb_define_method(rb_cProc, "yield", proc_call, -1); + rb_define_method(rb_cProc, "call_under", proc_call_under, -1); #endif rb_define_method(rb_cProc, "to_proc", proc_to_proc, 0); rb_define_method(rb_cProc, "arity", proc_arity, 0); diff --git a/vm_eval.c b/vm_eval.c index 6c26b97..562a215 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -1262,6 +1262,30 @@ yield_under(VALUE under, VALUE self, VALUE values) } } +static inline VALUE +invoke_block_from_c(rb_thread_t *th, const rb_block_t *block, + VALUE self, int argc, const VALUE *argv, + const rb_block_t *blockptr, const NODE *cref); + +VALUE +rb_proc_call_under(VALUE procval, VALUE under, VALUE self, VALUE values) +{ + rb_thread_t *th = GET_THREAD(); + rb_block_t block; + NODE *cref; + rb_proc_t *proc; + + GetProcPtr(procval, proc); + block = proc->block; + block.self = self; + cref = vm_cref_push(th, under, NOEX_PUBLIC, &proc->block); + cref->flags |= NODE_FL_CREF_PUSHED_BY_EVAL; + + return invoke_block_from_c(th, &block, self, + RARRAY_LENINT(values), RARRAY_PTR(values), + GC_GUARDED_PTR_REF(th->cfp->lfp[0]), cref); +} + /* string eval under the class/module context */ static VALUE eval_under(VALUE under, VALUE self, VALUE src, const char *file, int line) -- Yusuke Endoh ---------------------------------------- Feature #5007: Proc#call_under: Unifying instance_eval and instance_exec https://bugs.ruby-lang.org/issues/5007#change-25973 Author: judofyr (Magnus Holm) Status: Assigned Priority: Normal Assignee: matz (Yukihiro Matsumoto) Category: Target version: I'm proposing a method called Proc#call_under (the name could be discussed) which both unifies instance_eval and instance_exec, and makes it possible to call a Proc with a block and a scope: Proc#call_under(self, *args, &blk): proc { self }.call_under(1) # => 1 proc { |a| self + a }.call_under(1, 2) # => 3 proc { |&b| self + b.call }.call_under(2) { 2 } # => 4 -- http://bugs.ruby-lang.org/