Skip to content

Commit d4d99dd

Browse files
committed
Allow break from a block called by mrb_yield; close mruby#3359
This means mruby#3701 is now OK to merge.
1 parent 9e6a3f6 commit d4d99dd

4 files changed

Lines changed: 39 additions & 6 deletions

File tree

include/mruby/error.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_va
3232
/* declaration for fail method */
3333
MRB_API mrb_value mrb_f_raise(mrb_state*, mrb_value);
3434

35+
struct RBreak {
36+
MRB_OBJECT_HEADER;
37+
struct iv_tbl *iv;
38+
struct RProc *proc;
39+
mrb_value val;
40+
};
41+
3542
/**
3643
* Protect
3744
*

include/mruby/value.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ enum mrb_vtype {
116116
MRB_TT_DATA, /* 21 */
117117
MRB_TT_FIBER, /* 22 */
118118
MRB_TT_ISTRUCT, /* 23 */
119-
MRB_TT_MAXDEFINE /* 24 */
119+
MRB_TT_BREAK, /* 24 */
120+
MRB_TT_MAXDEFINE /* 25 */
120121
};
121122

122123
#include <mruby/object.h>

src/gc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ typedef struct {
112112
struct RProc proc;
113113
struct REnv env;
114114
struct RException exc;
115+
struct RBreak brk;
115116
#ifdef MRB_WORD_BOXING
116117
struct RFloat floatv;
117118
struct RCptr cptr;

src/vm.c

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,19 @@ mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const
760760
return mrb_exec_irep(mrb, self, p);
761761
}
762762

763+
static struct RBreak*
764+
break_new(mrb_state *mrb, struct RProc *p, mrb_value val)
765+
{
766+
struct RBreak *brk;
767+
768+
brk = (struct RBreak*)mrb_obj_alloc(mrb, MRB_TT_BREAK, NULL);
769+
brk->iv = NULL;
770+
brk->proc = p;
771+
brk->val = val;
772+
773+
return brk;
774+
}
775+
763776
typedef enum {
764777
LOCALJUMP_ERROR_RETURN = 0,
765778
LOCALJUMP_ERROR_BREAK = 1,
@@ -920,6 +933,8 @@ mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc)
920933

921934
if (exc_catched) {
922935
exc_catched = FALSE;
936+
if (mrb->exc && mrb->exc->tt == MRB_TT_BREAK)
937+
goto L_BREAK;
923938
goto L_RAISE;
924939
}
925940
mrb->jmp = &c_jmp;
@@ -1856,8 +1871,9 @@ mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc)
18561871
}
18571872
else {
18581873
int acc;
1859-
mrb_value v = regs[GETARG_A(i)];
1874+
mrb_value v;
18601875

1876+
v = regs[GETARG_A(i)];
18611877
mrb_gc_protect(mrb, v);
18621878
switch (GETARG_B(i)) {
18631879
case OP_R_RETURN:
@@ -1943,19 +1959,27 @@ mrb_vm_exec(mrb_state *mrb, struct RProc *proc, mrb_code *pc)
19431959
}
19441960
ARENA_RESTORE(mrb, ai);
19451961
mrb->c->vmexec = FALSE;
1962+
mrb->exc = (struct RObject*)break_new(mrb, proc, v);
19461963
mrb->jmp = prev_jmp;
1947-
return v;
1964+
MRB_THROW(prev_jmp);
1965+
}
1966+
if (FALSE) {
1967+
L_BREAK:
1968+
v = ((struct RBreak*)mrb->exc)->val;
1969+
proc = ((struct RBreak*)mrb->exc)->proc;
1970+
mrb->exc = NULL;
1971+
ci = mrb->c->ci;
19481972
}
19491973
mrb->c->stack = ci->stackent;
19501974
mrb->c->ci = mrb->c->cibase + proc->env->cioff + 1;
19511975
while (ci > mrb->c->ci) {
1976+
if (ci->env) {
1977+
mrb_env_unshare(mrb, ci->env);
1978+
}
19521979
if (ci[-1].acc == CI_ACC_SKIP) {
19531980
mrb->c->ci = ci;
19541981
goto L_BREAK_ERROR;
19551982
}
1956-
if (ci->env) {
1957-
mrb_env_unshare(mrb, ci->env);
1958-
}
19591983
ci--;
19601984
}
19611985
break;

0 commit comments

Comments
 (0)