Skip to content

Commit 6a0b68f

Browse files
committed
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()`.
1 parent 77c2aa7 commit 6a0b68f

File tree

3 files changed

+34
-2
lines changed

3 files changed

+34
-2
lines changed

include/mruby.h

+5
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,11 @@ MRB_API mrb_value mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg);
11211121
MRB_API mrb_value mrb_yield_argv(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv);
11221122
MRB_API mrb_value mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value *argv, mrb_value self, struct RClass *c);
11231123

1124+
/* continue execution to the proc */
1125+
/* this function should always be called as the last function of a method */
1126+
/* e.g. return mrb_yield_cont(mrb, proc, self, argc, argv); */
1127+
mrb_value mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv);
1128+
11241129
/* mrb_gc_protect() leaves the object in the arena */
11251130
MRB_API void mrb_gc_protect(mrb_state *mrb, mrb_value obj);
11261131
/* mrb_gc_register() keeps the object from GC. */

mrbgems/mruby-object-ext/src/object.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include <mruby.h>
22
#include <mruby/array.h>
33
#include <mruby/class.h>
4+
#include <mruby/proc.h>
45

56
/*
67
* call-seq:
@@ -86,7 +87,8 @@ mrb_obj_instance_exec(mrb_state *mrb, mrb_value self)
8687
}
8788
args = mrb_ary_new_from_values(mrb, argc, argv);
8889
argv = RARRAY_PTR(args);
89-
return mrb_yield_with_class(mrb, blk, argc, argv, self, c);
90+
mrb->c->ci->target_class = c;
91+
return mrb_yield_cont(mrb, blk, self, argc, argv);
9092
}
9193

9294
void

src/vm.c

+26-1
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p)
467467
{
468468
mrb_callinfo *ci = mrb->c->ci;
469469

470+
mrb->c->stack[0] = self;
470471
ci->proc = p;
471472
ci->target_class = p->target_class;
472473
if (MRB_PROC_CFUNC_P(p)) {
@@ -562,7 +563,8 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c)
562563
}
563564
ci = mrb->c->ci;
564565
if (ci->acc == CI_ACC_DIRECT) {
565-
return mrb_yield_with_class(mrb, blk, 1, &self, self, c);
566+
ci->target_class = c;
567+
return mrb_yield_cont(mrb, blk, self, 1, &self);
566568
}
567569
ci->target_class = c;
568570
p = mrb_proc_ptr(blk);
@@ -726,6 +728,29 @@ mrb_yield(mrb_state *mrb, mrb_value b, mrb_value arg)
726728
return mrb_yield_with_class(mrb, b, 1, &arg, p->env->stack[0], p->target_class);
727729
}
728730

731+
mrb_value
732+
mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const mrb_value *argv)
733+
{
734+
struct RProc *p;
735+
mrb_callinfo *ci;
736+
737+
if (mrb_nil_p(b)) {
738+
mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
739+
}
740+
if (mrb_type(b) != MRB_TT_PROC) {
741+
mrb_raise(mrb, E_TYPE_ERROR, "not a block");
742+
}
743+
744+
p = mrb_proc_ptr(b);
745+
ci = mrb->c->ci;
746+
747+
stack_extend(mrb, 3);
748+
mrb->c->stack[1] = mrb_ary_new_from_values(mrb, argc, argv);
749+
mrb->c->stack[2] = mrb_nil_value();
750+
ci->argc = -1;
751+
return mrb_exec_irep(mrb, self, p);
752+
}
753+
729754
typedef enum {
730755
LOCALJUMP_ERROR_RETURN = 0,
731756
LOCALJUMP_ERROR_BREAK = 1,

0 commit comments

Comments
 (0)