diff --git a/include/mruby/error.h b/include/mruby/error.h index 5f7ec43bd..ee6fe8ffc 100644 --- a/include/mruby/error.h +++ b/include/mruby/error.h @@ -37,19 +37,30 @@ MRB_API mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value st MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, const char *fmt, ...); #if defined(MRB_64BIT) || defined(MRB_USE_FLOAT32) || defined(MRB_NAN_BOXING) || defined(MRB_WORD_BOXING) +#undef MRB_USE_RBREAK_VALUE_UNION +#else +#define MRB_USE_RBREAK_VALUE_UNION 1 +#endif + +/* + * flags: + * 0..7: enum mrb_vtype (only when defined MRB_USE_RBREAK_VALUE_UNION) + * 8..10: RBREAK_TAGs in src/vm.c (otherwise, set to 0) + */ struct RBreak { MRB_OBJECT_HEADER; - const struct RProc *proc; + uintptr_t ci_break_index; // The top-level ci index to break. One before the return destination. +#ifndef MRB_USE_RBREAK_VALUE_UNION mrb_value val; +#else + union mrb_value_union value; +#endif }; + +#ifndef MRB_USE_RBREAK_VALUE_UNION #define mrb_break_value_get(brk) ((brk)->val) #define mrb_break_value_set(brk, v) ((brk)->val = v) #else -struct RBreak { - MRB_OBJECT_HEADER; - const struct RProc *proc; - union mrb_value_union value; -}; #define RBREAK_VALUE_TT_MASK ((1 << 8) - 1) static inline mrb_value mrb_break_value_get(struct RBreak *brk) @@ -66,9 +77,7 @@ mrb_break_value_set(struct RBreak *brk, mrb_value val) brk->flags &= ~RBREAK_VALUE_TT_MASK; brk->flags |= val.tt; } -#endif /* MRB_64BIT || MRB_USE_FLOAT32 || MRB_NAN_BOXING || MRB_WORD_BOXING */ -#define mrb_break_proc_get(brk) ((brk)->proc) -#define mrb_break_proc_set(brk, p) ((brk)->proc = p) +#endif /* MRB_USE_RBREAK_VALUE_UNION */ /** * Error check diff --git a/mrbgems/mruby-catch/src/catch.c b/mrbgems/mruby-catch/src/catch.c index 44852c6b5..d655ada90 100644 --- a/mrbgems/mruby-catch/src/catch.c +++ b/mrbgems/mruby-catch/src/catch.c @@ -6,81 +6,56 @@ #include #include -MRB_PRESYM_DEFINE_VAR_AND_INITER(catch_syms_3, 1, MRB_SYM(call)) -static const mrb_code catch_iseq_3[18] = { - OP_ENTER, 0x00, 0x00, 0x00, // 000 ENTER 0:0:0:0:0:0:0 (0x0) - OP_GETUPVAR, 0x02, 0x02, 0x01, // 004 GETUPVAR R2 2 1 - OP_GETUPVAR, 0x03, 0x01, 0x01, // 008 GETUPVAR R3 1 1 - OP_SEND, 0x02, 0x00, 0x01, // 012 SEND R2 :call n=1 - OP_RETURN, 0x02, // 016 RETURN R2 -}; -static const mrb_irep catch_irep_3 = { - 2,5,0, - MRB_IREP_STATIC,catch_iseq_3, - NULL,catch_syms_3,NULL, - NULL, - NULL, - 18,0,1,0,0 -}; -static const mrb_irep *catch_reps_2[1] = { - &catch_irep_3, -}; -static const mrb_code catch_iseq_2[13] = { - OP_ENTER, 0x00, 0x00, 0x00, // 000 ENTER 0:0:0:0:0:0:0 (0x0) - OP_LAMBDA, 0x02, 0x00, // 004 LAMBDA R2 I[0] - OP_SEND, 0x02, 0x00, 0x00, // 007 SEND R2 :call n=0 - OP_RETURN, 0x02, // 011 RETURN R2 -}; -static const mrb_irep catch_irep_2 = { - 2,4,0, - MRB_IREP_STATIC,catch_iseq_2, - NULL,catch_syms_3,catch_reps_2, - NULL, - NULL, - 13,0,1,1,0 -}; -static const mrb_irep *catch_reps_1[1] = { - &catch_irep_2, -}; -MRB_PRESYM_DEFINE_VAR_AND_INITER(catch_syms_1, 3, MRB_SYM(Object), MRB_SYM(new), MRB_SYM(call)) -static const mrb_code catch_iseq_1[29] = { +MRB_PRESYM_DEFINE_VAR_AND_INITER(catch_syms, 3, MRB_SYM(Object), MRB_SYM(new), MRB_SYM(call)) +/* + * def catch(r1 = Object.new, &r2) + * r2.call(r1) + * end + */ +static const mrb_code catch_iseq[] = { OP_ENTER, 0x00, 0x20, 0x01, // 000 ENTER 0:1:0:0:0:0:1 (0x2001) - OP_JMP, 0x00, 0x03, // 004 JMP 010 - OP_JMP, 0x00, 0x0a, // 007 JMP 020 - OP_GETCONST, 0x03, 0x00, // 010 GETCONST R3 Object - OP_SEND, 0x03, 0x01, 0x00, // 013 SEND R3 :new n=0 - OP_MOVE, 0x01, 0x03, // 017 MOVE R1 R3 - OP_LAMBDA, 0x03, 0x00, // 020 LAMBDA R3 I[0] - OP_SEND, 0x03, 0x02, 0x00, // 023 SEND R3 :call n=0 - OP_RETURN, 0x03, // 027 RETURN R3 + OP_JMP, 0x00, 0x06, // 004 JMP 013 + + // copy for block parameter "tag" when method argument are given + OP_MOVE, 0x03, 0x01, // 007 MOVE R3 R1 + OP_JMP, 0x00, 0x0a, // 010 JMP 023 + + // create a tag for default parameter + OP_GETCONST, 0x03, 0x00, // 013 GETCONST R3 Object + OP_SEND, 0x03, 0x01, 0x00, // 016 SEND R3 :new n=0 + OP_MOVE, 0x01, 0x03, // 020 MOVE R1 R3 + + // to save on the stack, block variables are used as is + OP_SEND, 0x02, 0x02, 0x01, // 023 SEND R2 :call n=1 + OP_RETURN, 0x02, // 027 RETURN R2 }; static const mrb_irep catch_irep = { 3,5,0, - MRB_IREP_STATIC,catch_iseq_1, - NULL,catch_syms_1,catch_reps_1, + MRB_IREP_STATIC,catch_iseq, + NULL,catch_syms,NULL, NULL, NULL, - 29,0,3,1,0 + sizeof(catch_iseq),0,3,0,0 }; static const struct RProc catch_proc = { NULL, NULL, MRB_TT_PROC, MRB_GC_RED, MRB_FL_OBJ_IS_FROZEN | MRB_PROC_SCOPE | MRB_PROC_STRICT, { &catch_irep }, NULL, { NULL } }; -static const mrb_callinfo * +static uintptr_t find_catcher(mrb_state *mrb, mrb_value tag) { - const mrb_callinfo *ci = mrb->c->ci; - size_t n = ci - mrb->c->cibase; - ci--; + const mrb_callinfo *ci = mrb->c->ci - 1; // skip ownself throw + ptrdiff_t n = ci - mrb->c->cibase; + for (; n > 0; n--, ci--) { const mrb_value *arg1 = ci->stack + 1; if (ci->proc == &catch_proc && mrb_obj_eq(mrb, *arg1, tag)) { - return ci; + return (uintptr_t)n; } } - return NULL; + return 0; } static mrb_value @@ -91,11 +66,11 @@ throw_m(mrb_state *mrb, mrb_value self) obj = mrb_nil_value(); } - const mrb_callinfo *ci = find_catcher(mrb, tag); - if (ci) { + uintptr_t ci_index = find_catcher(mrb, tag); + if (ci_index > 0) { struct RBreak *b = MRB_OBJ_ALLOC(mrb, MRB_TT_BREAK, NULL); mrb_break_value_set(b, obj); - mrb_break_proc_set(b, ci[2].proc); /* Back to the closure in `catch` method */ + b->ci_break_index = ci_index; /* Back to the caller directly */ mrb_exc_raise(mrb, mrb_obj_value(b)); } else { @@ -111,8 +86,7 @@ mrb_mruby_catch_gem_init(mrb_state *mrb) { mrb_method_t m; - MRB_PRESYM_INIT_SYMBOLS(mrb, catch_syms_3); - MRB_PRESYM_INIT_SYMBOLS(mrb, catch_syms_1); + MRB_PRESYM_INIT_SYMBOLS(mrb, catch_syms); MRB_METHOD_FROM_PROC(m, &catch_proc); mrb_define_method_raw(mrb, mrb->kernel_module, MRB_SYM(catch), m); diff --git a/src/gc.c b/src/gc.c index 7c4461e3b..9eb84311b 100644 --- a/src/gc.c +++ b/src/gc.c @@ -677,7 +677,6 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) case MRB_TT_BREAK: { struct RBreak *brk = (struct RBreak*)obj; - mrb_gc_mark(mrb, (struct RBasic*)mrb_break_proc_get(brk)); mrb_gc_mark_value(mrb, mrb_break_value_get(brk)); } break; diff --git a/src/vm.c b/src/vm.c index 6d8c74612..f03acfe81 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1104,12 +1104,12 @@ mrb_break_tag_set(struct RBreak *brk, uint32_t tag) } static struct RBreak* -break_new(mrb_state *mrb, uint32_t tag, const struct RProc *p, mrb_value val) +break_new(mrb_state *mrb, uint32_t tag, const mrb_callinfo *return_ci, mrb_value val) { - struct RBreak *brk; + mrb_assert((size_t)(return_ci - mrb->c->cibase) <= (size_t)(mrb->c->ci - mrb->c->cibase)); - brk = MRB_OBJ_ALLOC(mrb, MRB_TT_BREAK, NULL); - mrb_break_proc_set(brk, p); + struct RBreak *brk = MRB_OBJ_ALLOC(mrb, MRB_TT_BREAK, NULL); + brk->ci_break_index = return_ci - mrb->c->cibase; mrb_break_value_set(brk, val); mrb_break_tag_set(brk, tag); @@ -1213,27 +1213,27 @@ break_tag_p(struct RBreak *brk, uint32_t tag) } static void -prepare_tagged_break(mrb_state *mrb, uint32_t tag, const struct RProc *proc, mrb_value val) +prepare_tagged_break(mrb_state *mrb, uint32_t tag, const mrb_callinfo *return_ci, mrb_value val) { if (break_tag_p((struct RBreak*)mrb->exc, tag)) { mrb_break_tag_set((struct RBreak*)mrb->exc, tag); } else { - mrb->exc = (struct RObject*)break_new(mrb, tag, proc, val); + mrb->exc = (struct RObject*)break_new(mrb, tag, return_ci, val); } } -#define THROW_TAGGED_BREAK(mrb, tag, proc, val) \ +#define THROW_TAGGED_BREAK(mrb, tag, return_ci, val) \ do { \ - prepare_tagged_break(mrb, tag, proc, val); \ + prepare_tagged_break(mrb, tag, return_ci, val); \ goto L_CATCH_TAGGED_BREAK; \ } while (0) -#define UNWIND_ENSURE(mrb, ci, pc, tag, proc, val) \ +#define UNWIND_ENSURE(mrb, ci, pc, tag, return_ci, val) \ do { \ ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ENSURE); \ if (ch) { \ - THROW_TAGGED_BREAK(mrb, tag, proc, val); \ + THROW_TAGGED_BREAK(mrb, tag, return_ci, val); \ } \ } while (0) @@ -1696,7 +1696,7 @@ RETRY_TRY_BLOCK: if (ch) { /* avoiding a jump from a catch handler into the same handler */ if (a < mrb_irep_catch_handler_unpack(ch->begin) || a >= mrb_irep_catch_handler_unpack(ch->end)) { - THROW_TAGGED_BREAK(mrb, RBREAK_TAG_JUMP, proc, mrb_fixnum_value(a)); + THROW_TAGGED_BREAK(mrb, RBREAK_TAG_JUMP, mrb->c->ci, mrb_fixnum_value(a)); } } } @@ -2295,7 +2295,6 @@ RETRY_TRY_BLOCK: localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } - proc = dst; break; } /* fallthrough */ @@ -2348,18 +2347,14 @@ RETRY_TRY_BLOCK: if (ci == mrb->c->cibase) { goto L_BREAK_ERROR; } - proc = ci->proc; if (FALSE) { struct RBreak *brk; L_BREAK: brk = (struct RBreak*)mrb->exc; - proc = mrb_break_proc_get(brk); + ci = &mrb->c->cibase[brk->ci_break_index]; + proc = ci->proc; v = mrb_break_value_get(brk); - ci = mrb->c->ci; - while (mrb->c->cibase < ci && ci->proc != proc) { - ci--; - } pc = ci->pc; switch (mrb_break_tag_get(brk)) { @@ -2379,16 +2374,12 @@ RETRY_TRY_BLOCK: for (;;) { CHECKPOINT_RESTORE(RBREAK_TAG_BREAK) { struct RBreak *brk = (struct RBreak*)mrb->exc; - proc = mrb_break_proc_get(brk); - ci = mrb->c->ci; - while (mrb->c->cibase <= ci && ci->proc != proc) { - ci--; - } + ci = &mrb->c->cibase[brk->ci_break_index]; v = mrb_break_value_get(brk); mrb_gc_protect(mrb, v); } CHECKPOINT_MAIN(RBREAK_TAG_BREAK) { - UNWIND_ENSURE(mrb, mrb->c->ci, mrb->c->ci->pc, RBREAK_TAG_BREAK, proc, v); + UNWIND_ENSURE(mrb, mrb->c->ci, mrb->c->ci->pc, RBREAK_TAG_BREAK, ci, v); } CHECKPOINT_END(RBREAK_TAG_BREAK); @@ -2397,7 +2388,7 @@ RETRY_TRY_BLOCK: } cipop(mrb); if (mrb->c->ci[1].cci != CINFO_NONE) { - mrb->exc = (struct RObject*)break_new(mrb, RBREAK_TAG_BREAK, proc, v); + mrb->exc = (struct RObject*)break_new(mrb, RBREAK_TAG_BREAK, ci, v); mrb_gc_arena_restore(mrb, ai); mrb->c->vmexec = FALSE; mrb->jmp = prev_jmp; @@ -3092,7 +3083,7 @@ RETRY_TRY_BLOCK: /* do nothing */ } CHECKPOINT_MAIN(RBREAK_TAG_STOP) { - UNWIND_ENSURE(mrb, mrb->c->ci, pc, RBREAK_TAG_STOP, proc, mrb_nil_value()); + UNWIND_ENSURE(mrb, mrb->c->ci, mrb->c->ci->pc, RBREAK_TAG_STOP, mrb->c->ci, mrb_nil_value()); } CHECKPOINT_END(RBREAK_TAG_STOP); L_STOP: