From 0a9388faa3c52bc352796f61d8c57eeb6bdb5c24 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 1 Mar 2014 18:13:10 +0900 Subject: [PATCH] allow send method not to call mrb_funcall if calling method is implemented in Ruby; fix #1680 ref #1765 --- include/mruby/proc.h | 3 +++ src/backtrace.c | 1 + src/kernel.c | 30 --------------------- src/vm.c | 62 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 30 deletions(-) diff --git a/include/mruby/proc.h b/include/mruby/proc.h index 602b522887..474cf01428 100644 --- a/include/mruby/proc.h +++ b/include/mruby/proc.h @@ -52,6 +52,9 @@ struct RProc *mrb_closure_new(mrb_state*, mrb_irep*); struct RProc *mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals); void mrb_proc_copy(struct RProc *a, struct RProc *b); +/* implementation of #send method */ +mrb_value mrb_f_send(mrb_state *mrb, mrb_value self); + #include "mruby/khash.h" KHASH_DECLARE(mt, mrb_sym, struct RProc*, 1) diff --git a/src/backtrace.c b/src/backtrace.c index 6469fc0698..c18a7cb95b 100644 --- a/src/backtrace.c +++ b/src/backtrace.c @@ -72,6 +72,7 @@ output_backtrace(mrb_state *mrb, mrb_int ciidx, mrb_code *pc0, output_stream_fun filename = NULL; lineno = -1; + if (!ci->proc) continue; if (MRB_PROC_CFUNC_P(ci->proc)) { continue; } diff --git a/src/kernel.c b/src/kernel.c index e61a602beb..45cc299d2b 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -157,36 +157,6 @@ mrb_obj_id_m(mrb_state *mrb, mrb_value self) return mrb_fixnum_value(mrb_obj_id(self)); } -/* 15.3.1.3.4 */ -/* 15.3.1.3.44 */ -/* - * call-seq: - * obj.send(symbol [, args...]) -> obj - * obj.__send__(symbol [, args...]) -> obj - * - * Invokes the method identified by _symbol_, passing it any - * arguments specified. You can use __send__ if the name - * +send+ clashes with an existing method in _obj_. - * - * class Klass - * def hello(*args) - * "Hello " + args.join(' ') - * end - * end - * k = Klass.new - * k.send :hello, "gentle", "readers" #=> "Hello gentle readers" - */ -static mrb_value -mrb_f_send(mrb_state *mrb, mrb_value self) -{ - mrb_sym name; - mrb_value block, *argv; - int argc; - - mrb_get_args(mrb, "n*&", &name, &argv, &argc, &block); - return mrb_funcall_with_block(mrb,self, name, argc, argv, block); -} - /* 15.3.1.2.2 */ /* 15.3.1.2.5 */ /* 15.3.1.3.6 */ diff --git a/src/vm.c b/src/vm.c index 8bf34b1708..62ac86c90a 100644 --- a/src/vm.c +++ b/src/vm.c @@ -417,6 +417,68 @@ mrb_funcall_argv(mrb_state *mrb, mrb_value self, mrb_sym mid, int argc, mrb_valu return mrb_funcall_with_block(mrb, self, mid, argc, argv, mrb_nil_value()); } +/* 15.3.1.3.4 */ +/* 15.3.1.3.44 */ +/* + * call-seq: + * obj.send(symbol [, args...]) -> obj + * obj.__send__(symbol [, args...]) -> obj + * + * Invokes the method identified by _symbol_, passing it any + * arguments specified. You can use __send__ if the name + * +send+ clashes with an existing method in _obj_. + * + * class Klass + * def hello(*args) + * "Hello " + args.join(' ') + * end + * end + * k = Klass.new + * k.send :hello, "gentle", "readers" #=> "Hello gentle readers" + */ +mrb_value +mrb_f_send(mrb_state *mrb, mrb_value self) +{ + mrb_sym name; + mrb_value block, *argv, *regs; + int argc, i, len; + struct RProc *p; + struct RClass *c; + mrb_callinfo *ci; + + mrb_get_args(mrb, "n*&", &name, &argv, &argc, &block); + + c = mrb_class(mrb, self); + p = mrb_method_search_vm(mrb, &c, name); + if (!p || MRB_PROC_CFUNC_P(p)) { + return mrb_funcall_with_block(mrb, self, name, argc, argv, block); + } + + ci = mrb->c->ci; + ci->mid = name; + ci->target_class = c; + ci->proc = p; + regs = mrb->c->stack+1; + /* remove first symbol from arguments */ + if (ci->argc >= 0) { + for (i=0,len=ci->argc; iargc--; + } + else { /* variable length arguments */ + mrb_ary_shift(mrb, regs[0]); + } + cipush(mrb); + ci = mrb->c->ci; + ci->target_class = 0; + ci->pc = p->body.irep->iseq; + ci->stackent = mrb->c->stack; + ci->acc = 0; + + return self; +} + mrb_value mrb_yield_internal(mrb_state *mrb, mrb_value b, int argc, mrb_value *argv, mrb_value self, struct RClass *c) {