Skip to content

Commit

Permalink
allow send method not to call mrb_funcall if calling method is implem…
Browse files Browse the repository at this point in the history
…ented in Ruby; fix mruby#1680 ref mruby#1765
  • Loading branch information
matz authored and cremno committed Mar 4, 2014
1 parent 660c5f0 commit 0a9388f
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 30 deletions.
3 changes: 3 additions & 0 deletions include/mruby/proc.h
Expand Up @@ -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)

Expand Down
1 change: 1 addition & 0 deletions src/backtrace.c
Expand Up @@ -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;
}
Expand Down
30 changes: 0 additions & 30 deletions src/kernel.c
Expand Up @@ -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 <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"
*/
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 */
Expand Down
62 changes: 62 additions & 0 deletions src/vm.c
Expand Up @@ -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)
{
Expand Down

0 comments on commit 0a9388f

Please sign in to comment.