diff --git a/include/mruby.h b/include/mruby.h index 06635b79e..642716477 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -178,6 +178,7 @@ typedef struct { uint8_t cci; /* called from C function */ mrb_sym mid; const struct RProc *proc; + struct RProc *blk; mrb_value *stack; const mrb_code *pc; /* current address on iseq of this proc */ union { diff --git a/src/vm.c b/src/vm.c index 3cf919097..38784554f 100644 --- a/src/vm.c +++ b/src/vm.c @@ -328,9 +328,11 @@ mrb_vm_ci_env_set(mrb_callinfo *ci, struct REnv *e) #define CINFO_DIRECT 2 #define CINFO_RESUMED 3 +#define BLK_PTR(b) ((mrb_proc_p(b)) ? mrb_proc_ptr(b) : NULL) + static inline mrb_callinfo* -cipush(mrb_state *mrb, mrb_int push_stacks, uint8_t cci, - struct RClass *target_class, const struct RProc *proc, mrb_sym mid, uint16_t argc) +cipush(mrb_state *mrb, mrb_int push_stacks, uint8_t cci, struct RClass *target_class, + const struct RProc *proc, struct RProc *blk, mrb_sym mid, uint16_t argc) { struct mrb_context *c = mrb->c; mrb_callinfo *ci = c->ci; @@ -348,6 +350,7 @@ cipush(mrb_state *mrb, mrb_int push_stacks, uint8_t cci, ci = ++c->ci; ci->mid = mid; CI_PROC_SET(ci, proc); + ci->blk = blk; ci->stack = ci[-1].stack + push_stacks; ci->n = argc & 0xf; ci->nk = (argc>>4) & 0xf; @@ -406,6 +409,10 @@ cipop(mrb_state *mrb) struct REnv *env = CI_ENV(c->ci); mrb_vm_ci_env_set(c->ci, NULL); // make possible to free by GC if env is not needed + struct RProc *b = c->ci->blk; + if (b && !MRB_PROC_STRICT_P(b) && MRB_PROC_ENV(b) == CI_ENV(&c->ci[-1])) { + b->flags |= MRB_PROC_ORPHAN; + } if (env && !mrb_env_unshare(mrb, env, TRUE)) { c->ci--; // exceptions are handled at the method caller; see #3087 mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); @@ -613,6 +620,16 @@ funcall_args_capture(mrb_state *mrb, int stoff, mrb_int argc, const mrb_value *a } } +static mrb_value +ensure_block(mrb_state *mrb, mrb_value blk) +{ + if (!mrb_nil_p(blk) && !mrb_proc_p(blk)) { + blk = mrb_type_convert(mrb, blk, MRB_TT_PROC, MRB_SYM(to_proc)); + /* The stack might have been reallocated during mrb_type_convert(), see #3622 */ + } + return blk; +} + MRB_API mrb_value mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv, mrb_value blk) { @@ -650,7 +667,8 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc if (ci - mrb->c->cibase > MRB_CALL_LEVEL_MAX) { mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); } - ci = cipush(mrb, n, CINFO_DIRECT, NULL, NULL, 0, 0); + blk = ensure_block(mrb, blk); + ci = cipush(mrb, n, CINFO_DIRECT, NULL, NULL, BLK_PTR(blk), 0, 0); funcall_args_capture(mrb, 0, argc, argv, blk, ci); ci->u.target_class = mrb_class(mrb, self); m = mrb_method_search_vm(mrb, &ci->u.target_class, mid); @@ -724,7 +742,7 @@ exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p) stack_clear(ci->stack+keep, nregs-keep); } - cipush(mrb, 0, 0, NULL, NULL, 0, 0); + cipush(mrb, 0, 0, NULL, NULL, NULL, 0, 0); return self; } @@ -742,7 +760,7 @@ mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p) if (MRB_PROC_NOARG_P(p)) { check_method_noarg(mrb, ci); } - cipush(mrb, 0, CINFO_DIRECT, CI_TARGET_CLASS(ci), p, ci->mid, ci->n|(ci->nk<<4)); + cipush(mrb, 0, CINFO_DIRECT, CI_TARGET_CLASS(ci), p, NULL, ci->mid, ci->n|(ci->nk<<4)); ret = MRB_PROC_CFUNC(p)(mrb, self); cipop(mrb); } @@ -887,7 +905,7 @@ eval_under(mrb_state *mrb, mrb_value self, mrb_value blk, struct RClass *c) mrb->c->ci->stack[0] = self; mrb->c->ci->stack[1] = self; stack_clear(mrb->c->ci->stack+2, nregs-2); - ci = cipush(mrb, 0, 0, NULL, NULL, 0, 0); + ci = cipush(mrb, 0, 0, NULL, NULL, NULL, 0, 0); return self; } @@ -958,7 +976,7 @@ mrb_yield_with_class(mrb_state *mrb, mrb_value b, mrb_int argc, const mrb_value ci = mrb->c->ci; n = mrb_ci_nregs(ci); p = mrb_proc_ptr(b); - ci = cipush(mrb, n, CINFO_DIRECT, NULL, NULL, 0, 0); + ci = cipush(mrb, n, CINFO_DIRECT, NULL, NULL, NULL, 0, 0); funcall_args_capture(mrb, 0, argc, argv, mrb_nil_value(), ci); ci->u.target_class = c; ci->mid = mid; @@ -1714,11 +1732,7 @@ RETRY_TRY_BLOCK: SET_NIL_VALUE(blk); } else { - blk = regs[bidx]; - if (!mrb_nil_p(blk) && !mrb_proc_p(blk)) { - blk = mrb_type_convert(mrb, blk, MRB_TT_PROC, MRB_SYM(to_proc)); - /* The stack might have been reallocated during mrb_type_convert(), see #3622 */ - } + blk = ensure_block(mrb, regs[bidx]); regs[new_bidx] = blk; } @@ -1726,7 +1740,7 @@ RETRY_TRY_BLOCK: ci = mrb->c->ci; cls = (insn == OP_SUPER) ? CI_TARGET_CLASS(ci)->super : mrb_class(mrb, recv); m = mrb_method_search_vm(mrb, &cls, mid); - ci = cipush(mrb, a, CINFO_DIRECT, NULL, NULL, 0, c); + ci = cipush(mrb, a, CINFO_DIRECT, NULL, NULL, BLK_PTR(blk), 0, c); if (MRB_METHOD_UNDEF_P(m)) { m = prepare_missing(mrb, recv, mid, &cls, 0, &c, blk, (insn == OP_SUPER ? 1 : 0)); mid = MRB_SYM(method_missing); @@ -1736,6 +1750,7 @@ RETRY_TRY_BLOCK: ci->cci = CINFO_NONE; ci->mid = mid; ci->u.target_class = cls; + if (!mrb_nil_p(blk)) ci->blk = mrb_proc_ptr(blk); if (MRB_METHOD_CFUNC_P(m)) { if (MRB_METHOD_PROC_P(m)) { @@ -1752,12 +1767,6 @@ RETRY_TRY_BLOCK: mrb_gc_arena_shrink(mrb, ai); if (mrb->exc) goto L_RAISE; ci = mrb->c->ci; - if (mrb_proc_p(blk)) { - struct RProc *p = mrb_proc_ptr(blk); - if (p && !MRB_PROC_STRICT_P(p) && MRB_PROC_ENV(p) == CI_ENV(&ci[-1])) { - p->flags |= MRB_PROC_ORPHAN; - } - } if (!ci->u.target_class) { /* return from context modifying method (resume/yield) */ if (ci->cci == CINFO_RESUMED) { mrb->jmp = prev_jmp; @@ -2126,19 +2135,6 @@ RETRY_TRY_BLOCK: mrb_callinfo *ci; ci = mrb->c->ci; - if (ci->mid) { - mrb_value blk = regs[mrb_ci_bidx(ci)]; - - if (mrb_proc_p(blk)) { - struct RProc *p = mrb_proc_ptr(blk); - - if (!MRB_PROC_STRICT_P(p) && - ci > mrb->c->cibase && MRB_PROC_ENV(p) == CI_ENV(&ci[-1])) { - p->flags |= MRB_PROC_ORPHAN; - } - } - } - if (mrb->exc) { L_RAISE: ci = mrb->c->ci; @@ -2946,7 +2942,7 @@ RETRY_TRY_BLOCK: p->flags |= MRB_PROC_SCOPE; /* prepare call stack */ - cipush(mrb, a, 0, mrb_class_ptr(recv), p, 0, 0); + cipush(mrb, a, 0, mrb_class_ptr(recv), p, NULL, 0, 0); irep = p->body.irep; pool = irep->pool; @@ -3106,7 +3102,7 @@ mrb_top_run(mrb_state *mrb, const struct RProc *proc, mrb_value self, mrb_int st mrb_vm_ci_env_set(mrb->c->ci, NULL); return mrb_vm_run(mrb, proc, self, stack_keep); } - cipush(mrb, 0, CINFO_SKIP, mrb->object_class, NULL, 0, 0); + cipush(mrb, 0, CINFO_SKIP, mrb->object_class, NULL, NULL, 0, 0); v = mrb_vm_run(mrb, proc, self, stack_keep); return v;