Permalink
Browse files

Allow `break` from a block called by `mrb_yield`; close #3359

This means #3701 is now OK to merge.
  • Loading branch information...
matz committed Jun 16, 2017
1 parent 9e6a3f6 commit d4d99dd6d7e1374af3e567b175035b36977337c4
Showing with 39 additions and 6 deletions.
  1. +7 −0 include/mruby/error.h
  2. +2 −1 include/mruby/value.h
  3. +1 −0 src/gc.c
  4. +29 −5 src/vm.c
View
@@ -32,6 +32,13 @@ MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_va
/* declaration for fail method */
MRB_API mrb_value mrb_f_raise(mrb_state*, mrb_value);
struct RBreak {
MRB_OBJECT_HEADER;
struct iv_tbl *iv;
struct RProc *proc;
mrb_value val;
};
/**
* Protect
*
View
@@ -116,7 +116,8 @@ enum mrb_vtype {
MRB_TT_DATA, /* 21 */
MRB_TT_FIBER, /* 22 */
MRB_TT_ISTRUCT, /* 23 */
MRB_TT_MAXDEFINE /* 24 */
MRB_TT_BREAK, /* 24 */
MRB_TT_MAXDEFINE /* 25 */
};
#include <mruby/object.h>
View
@@ -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;
View
@@ -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 @@ mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc)
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 @@ mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc)
}
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 @@ mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc)
}
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;

0 comments on commit d4d99dd

Please sign in to comment.