I'll leave here a patch to include the OP_REDO instruction based on this PR.
This patch also tests successfully.
diff --git a/include/mruby/ops.h b/include/mruby/ops.h index cda796f08..56e430a33 100644 --- a/include/mruby/ops.h +++ b/include/mruby/ops.h @@ -54,6 +54,7 @@ OPCODE(JMPIF, BS) /* if R[a] pc+=b */ OPCODE(JMPNOT, BS) /* if !R[a] pc+=b */ OPCODE(JMPNIL, BS) /* if R[a]==nil pc+=b */ OPCODE(JMPUW, S) /* unwind_and_jump_to(a) */ +OPCODE(REDO, S) /* pc+=b (with ensure handler) */ OPCODE(EXCEPT, B) /* R[a] = exc */ OPCODE(RESCUE, BB) /* R[b] = R[a].isa?(R[b]) */ OPCODE(RAISEIF, B) /* raise(R[a]) if R[a] */ diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index 364ce2048..05e1c9260 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -1323,7 +1323,6 @@ for_body(codegen_scope *s, node *tree) /* construct loop */ lp = loop_push(s, LOOP_FOR); lp->pc1 = new_label(s); - genop_0(s, OP_NOP); /* for redo */ /* loop body */ codegen(s, tree->cdr->cdr->car, VAL); @@ -2590,7 +2589,6 @@ codegen(codegen_scope *s, node *tree, int val) pos = genjmp2_0(s, OP_JMPIF, cursp(), NOVAL); } lp->pc1 = new_label(s); - genop_0(s, OP_NOP); /* for redo */ codegen(s, tree->cdr, NOVAL); genjmp(s, OP_JMP, lp->pc0); dispatch(s, pos); @@ -3190,7 +3188,7 @@ codegen(codegen_scope *s, node *tree, int val) raise_error(s, "unexpected redo"); } if (lp->type != LOOP_BEGIN && lp->type != LOOP_RESCUE) { - genjmp(s, OP_JMPUW, lp->pc1); + genjmp(s, OP_REDO, lp->pc1); break; } } diff --git a/src/codedump.c b/src/codedump.c index 556e4f62d..f9e6b08c4 100644 --- a/src/codedump.c +++ b/src/codedump.c @@ -308,6 +308,10 @@ codedump(mrb_state *mrb, const mrb_irep *irep, FILE *out) i = pc - irep->iseq; fprintf(out, "JMPUW\t\t%03d\n", (int)i+(int16_t)a); break; + CASE(OP_REDO, S): + i = pc - irep->iseq; + fprintf(out, "REDO\t\t%03d\n", (int)i+(int16_t)a); + break; CASE(OP_JMPIF, BS): i = pc - irep->iseq; fprintf(out, "JMPIF\t\tR%d\t%03d\t", a, (int)i+(int16_t)b); diff --git a/src/vm.c b/src/vm.c index 4d78035ad..54db0c8e8 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1124,7 +1124,8 @@ mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const #define RBREAK_TAG_FOREACH(f) \ f(RBREAK_TAG_BREAK, 0) \ f(RBREAK_TAG_JUMP, 1) \ - f(RBREAK_TAG_STOP, 2) + f(RBREAK_TAG_REDO, 2) \ + f(RBREAK_TAG_STOP, 3) #define RBREAK_TAG_DEFINE(tag, i) tag = i, enum { @@ -1733,6 +1734,29 @@ RETRY_TRY_BLOCK: JUMP; } + // TODO: OP_JMPUW との共通処理が出来るかもしれない + CASE(OP_REDO, S) { + a = (uint32_t)((ci->pc - irep->iseq) + (int16_t)a); + CHECKPOINT_RESTORE(RBREAK_TAG_REDO) { + struct RBreak *brk = (struct RBreak*)mrb->exc; + mrb_value target = mrb_break_value_get(brk); + mrb_assert(mrb_integer_p(target)); + a = (uint32_t)mrb_integer(target); + mrb_assert(a >= 0 && a < irep->ilen); + } + CHECKPOINT_MAIN(RBREAK_TAG_REDO) { + if (irep->clen > 0 && + (ch = catch_handler_find(irep, ci->pc, MRB_CATCH_FILTER_ENSURE))) { + THROW_TAGGED_BREAK(mrb, RBREAK_TAG_REDO, mrb->c->ci, mrb_fixnum_value(a)); + } + } + CHECKPOINT_END(RBREAK_TAG_REDO); + + mrb->exc = NULL; /* clear break object */ + ci->pc = irep->iseq + a; + JUMP; + } + CASE(OP_EXCEPT, B) { mrb_value exc;