mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
Use trampoline technique for instance_exec; ref #3359
A new function `mrb_yield_cont()` is provided. You have to call it at the end of a C defined method, e.g. `return mrb_yield_cont()`.
This commit is contained in:
@@ -467,6 +467,7 @@ mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
|
||||
{
|
||||
mrb_callinfo *ci = mrb->c->ci;
|
||||
|
||||
mrb->c->stack[0] = self;
|
||||
ci->proc = p;
|
||||
ci->target_class = p->target_class;
|
||||
if (MRB_PROC_CFUNC_P(p)) {
|
||||
@@ -562,7 +563,8 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c)
|
||||
}
|
||||
ci = mrb->c->ci;
|
||||
if (ci->acc == CI_ACC_DIRECT) {
|
||||
return mrb_yield_with_class(mrb, blk, 1, &self, self, c);
|
||||
ci->target_class = c;
|
||||
return mrb_yield_cont(mrb, blk, self, 1, &self);
|
||||
}
|
||||
ci->target_class = c;
|
||||
p = mrb_proc_ptr(blk);
|
||||
@@ -726,6 +728,29 @@ mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg)
|
||||
return mrb_yield_with_class(mrb, b, 1, &arg, p->env->stack[0], p->target_class);
|
||||
}
|
||||
|
||||
mrb_value
|
||||
mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv)
|
||||
{
|
||||
struct RProc *p;
|
||||
mrb_callinfo *ci;
|
||||
|
||||
if (mrb_nil_p(b)) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
|
||||
}
|
||||
if (mrb_type(b) != MRB_TT_PROC) {
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "not a block");
|
||||
}
|
||||
|
||||
p = mrb_proc_ptr(b);
|
||||
ci = mrb->c->ci;
|
||||
|
||||
stack_extend(mrb, 3);
|
||||
mrb->c->stack[1] = mrb_ary_new_from_values(mrb, argc, argv);
|
||||
mrb->c->stack[2] = mrb_nil_value();
|
||||
ci->argc = -1;
|
||||
return mrb_exec_irep(mrb, self, p);
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
LOCALJUMP_ERROR_RETURN = 0,
|
||||
LOCALJUMP_ERROR_BREAK = 1,
|
||||
|
||||
Reference in New Issue
Block a user