diff options
| author | Yukihiro "Matz" Matsumoto <[email protected]> | 2017-06-16 11:29:47 +0900 |
|---|---|---|
| committer | Yukihiro "Matz" Matsumoto <[email protected]> | 2017-06-16 11:34:11 +0900 |
| commit | d4d99dd6d7e1374af3e567b175035b36977337c4 (patch) | |
| tree | 663ff774215ec09dedc2775b5c63c706ce7116aa /src | |
| parent | 9e6a3f6c959bad02093cbe527b4ae753271f463e (diff) | |
| download | mruby-d4d99dd6d7e1374af3e567b175035b36977337c4.tar.gz mruby-d4d99dd6d7e1374af3e567b175035b36977337c4.zip | |
Allow `break` from a block called by `mrb_yield`; close #3359
This means #3701 is now OK to merge.
Diffstat (limited to 'src')
| -rw-r--r-- | src/gc.c | 1 | ||||
| -rw-r--r-- | src/vm.c | 34 |
2 files changed, 30 insertions, 5 deletions
@@ -112,6 +112,7 @@ typedef struct { struct RProc proc; struct REnv env; struct RException exc; + struct RBreak brk; #ifdef MRB_WORD_BOXING struct RFloat floatv; struct RCptr cptr; @@ -760,6 +760,19 @@ mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const return mrb_exec_irep(mrb, self, p); } +static struct RBreak* +break_new(mrb_state *mrb, struct RProc *p, mrb_value val) +{ + struct RBreak *brk; + + brk = (struct RBreak*)mrb_obj_alloc(mrb, MRB_TT_BREAK, NULL); + brk->iv = NULL; + brk->proc = p; + brk->val = val; + + return brk; +} + typedef enum { LOCALJUMP_ERROR_RETURN = 0, LOCALJUMP_ERROR_BREAK = 1, @@ -920,6 +933,8 @@ RETRY_TRY_BLOCK: if (exc_catched) { exc_catched = FALSE; + if (mrb->exc && mrb->exc->tt == MRB_TT_BREAK) + goto L_BREAK; goto L_RAISE; } mrb->jmp = &c_jmp; @@ -1856,8 +1871,9 @@ RETRY_TRY_BLOCK: } else { int acc; - mrb_value v = regs[GETARG_A(i)]; + mrb_value v; + v = regs[GETARG_A(i)]; mrb_gc_protect(mrb, v); switch (GETARG_B(i)) { case OP_R_RETURN: @@ -1943,19 +1959,27 @@ RETRY_TRY_BLOCK: } ARENA_RESTORE(mrb, ai); mrb->c->vmexec = FALSE; + mrb->exc = (struct RObject*)break_new(mrb, proc, v); mrb->jmp = prev_jmp; - return v; + MRB_THROW(prev_jmp); + } + if (FALSE) { + L_BREAK: + v = ((struct RBreak*)mrb->exc)->val; + proc = ((struct RBreak*)mrb->exc)->proc; + mrb->exc = NULL; + ci = mrb->c->ci; } mrb->c->stack = ci->stackent; mrb->c->ci = mrb->c->cibase + proc->env->cioff + 1; while (ci > mrb->c->ci) { + if (ci->env) { + mrb_env_unshare(mrb, ci->env); + } if (ci[-1].acc == CI_ACC_SKIP) { mrb->c->ci = ci; goto L_BREAK_ERROR; } - if (ci->env) { - mrb_env_unshare(mrb, ci->env); - } ci--; } break; |
