summaryrefslogtreecommitdiffhomepage
path: root/src/vm.c
diff options
context:
space:
mode:
authorYukihiro "Matz" Matsumoto <[email protected]>2017-06-16 11:29:47 +0900
committerYukihiro "Matz" Matsumoto <[email protected]>2017-06-16 11:34:11 +0900
commitd4d99dd6d7e1374af3e567b175035b36977337c4 (patch)
tree663ff774215ec09dedc2775b5c63c706ce7116aa /src/vm.c
parent9e6a3f6c959bad02093cbe527b4ae753271f463e (diff)
downloadmruby-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/vm.c')
-rw-r--r--src/vm.c34
1 files changed, 29 insertions, 5 deletions
diff --git a/src/vm.c b/src/vm.c
index 9607d3c32..9b9cb3869 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -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;