summaryrefslogtreecommitdiffhomepage
path: root/src/vm.c
diff options
context:
space:
mode:
authorYukihiro "Matz" Matsumoto <[email protected]>2014-03-01 18:13:10 +0900
committerYukihiro "Matz" Matsumoto <[email protected]>2014-03-01 18:13:10 +0900
commit675fd3dc801f17a4e81aa1bd15925500246fd5e5 (patch)
treec0c102c6a284d8d6640356b4d38c3d0196c04784 /src/vm.c
parent8e170ce915626bb57ecad7e54d8fe207676d593e (diff)
downloadmruby-675fd3dc801f17a4e81aa1bd15925500246fd5e5.tar.gz
mruby-675fd3dc801f17a4e81aa1bd15925500246fd5e5.zip
allow send method not to call mrb_funcall if calling method is implemented in Ruby; fix #1680 ref #1765
Diffstat (limited to 'src/vm.c')
-rw-r--r--src/vm.c62
1 files changed, 62 insertions, 0 deletions
diff --git a/src/vm.c b/src/vm.c
index 8bf34b170..62ac86c90 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 <code>__send__</code> 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; i<len; i++) {
+ regs[i] = regs[i+1];
+ }
+ ci->argc--;
+ }
+ 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)
{