From 3ac682b2de45e7267a6f0efb47d45a785bd90b23 Mon Sep 17 00:00:00 2001 From: dearblue Date: Sat, 24 Jan 2026 11:31:32 +0900 Subject: [PATCH 1/3] Add the `MRB_ENSURE()` macro --- include/mruby.h | 4 ++++ include/mruby/error.h | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/mruby.h b/include/mruby.h index 542215541..b5a66dc18 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -1615,6 +1615,10 @@ mrbmemset(void *s, int c, size_t n) #define mrb_int_hash_func(mrb,key) (uint32_t)((key)^((key)<<2)^((key)>>2)) +#define MRB_UNIQNAME(name) MRB_UNIQNAME_1(name, __LINE__) +#define MRB_UNIQNAME_1(name, line) MRB_UNIQNAME_2(name, line) +#define MRB_UNIQNAME_2(name, line) name##line + MRB_END_DECL #endif /* MRUBY_H */ diff --git a/include/mruby/error.h b/include/mruby/error.h index e8c63dc3e..ba091fcfe 100644 --- a/include/mruby/error.h +++ b/include/mruby/error.h @@ -127,6 +127,42 @@ MRB_API mrb_value mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_val mrb_func_t rescue, mrb_value r_data, mrb_int len, struct RClass **classes); +/** + * Calls `func` via `mrb_protect_error()` and then always executes the user block exactly once. + * Even if a global jump (similar to a Ruby exception) occurs within `func`, the block will be executed, + * and after the block's completion, the global jump will be re-thrown. + * + * By checking `mrb->exc != NULL` within the block, you can determine if a global jump occurred in `func`. + * + * If you want to suppress the global jump and continue processing, use `mrb_clear_error(mrb); break;`. + * + * - `mrb`: The mruby state reference + * - `result_var`: Pre-defined mrb_value type variable (to receive `func`'s return value) + * - `func`: Function to call (compatible with `mrb_protect_error_func`) + * - `data`: User data to pass to `func` + * + * Example: + * + * mrb_value result; + * MRB_ENSURE(mrb, result, body_func, userdata) { + * // This block is always executed (equivalent to Ruby's ensure) + * + * if (mrb->exc) { + * // Post-processing when an exception occurs + * } + * + * // To ignore the global jump, use `mrb_clear_error(mrb); break;` here + * } + */ +#define MRB_ENSURE(mrb, result_var, func, data) \ + for (mrb_bool MRB_UNIQNAME(_break_) = FALSE; \ + !MRB_UNIQNAME(_break_) && \ + (((result_var) = mrb_protect_error(mrb, func, data, &MRB_UNIQNAME(_break_))), \ + ((mrb)->exc = (MRB_UNIQNAME(_break_) ? mrb_obj_ptr((result_var)) : NULL)), \ + TRUE); \ + (void)(MRB_UNIQNAME(_break_) && (mrb)->jmp && (mrb_exc_raise(mrb, result_var), TRUE)), \ + MRB_UNIQNAME(_break_) = TRUE) + MRB_END_DECL #endif /* MRUBY_ERROR_H */ From da75e4b049972b594a66e70503d52a920a6141cc Mon Sep 17 00:00:00 2001 From: dearblue Date: Sat, 24 Jan 2026 11:32:57 +0900 Subject: [PATCH 2/3] Use `MRB_ENSURE()` instead of `mrb_ensure()` The purpose is to avoid using the `MRB_TT_CPTR` object. The reasons are as follows: - The `MRB_WORD_BOXING` setting involves object creation. - If object creation fails, the `ary_set_t` data leaks memory. --- mrbgems/mruby-array-ext/mrbgem.rake | 1 - mrbgems/mruby-array-ext/src/array.c | 87 +++++++++-------------------- 2 files changed, 27 insertions(+), 61 deletions(-) diff --git a/mrbgems/mruby-array-ext/mrbgem.rake b/mrbgems/mruby-array-ext/mrbgem.rake index 17dff02ab..882caf1ab 100644 --- a/mrbgems/mruby-array-ext/mrbgem.rake +++ b/mrbgems/mruby-array-ext/mrbgem.rake @@ -2,5 +2,4 @@ MRuby::Gem::Specification.new('mruby-array-ext') do |spec| spec.license = 'MIT' spec.author = 'mruby developers' spec.summary = 'Array class extension' - spec.add_dependency 'mruby-error' end diff --git a/mrbgems/mruby-array-ext/src/array.c b/mrbgems/mruby-array-ext/src/array.c index fc82ecc10..5a6fe528d 100644 --- a/mrbgems/mruby-array-ext/src/array.c +++ b/mrbgems/mruby-array-ext/src/array.c @@ -452,9 +452,9 @@ struct ary_subtract_ctx { }; static mrb_value -ary_subtract_body(mrb_state *mrb, mrb_value data) +ary_subtract_body(mrb_state *mrb, void *data) { - struct ary_subtract_ctx *ctx = (struct ary_subtract_ctx *)mrb_cptr(data); + struct ary_subtract_ctx *ctx = (struct ary_subtract_ctx *)data; for (mrb_int i = 0; i < ctx->argc; i++) { ary_populate_temp_set(mrb, ctx->set, ctx->argv[i]); @@ -471,14 +471,6 @@ ary_subtract_body(mrb_state *mrb, mrb_value data) return ctx->result; } -static mrb_value -ary_subtract_ensure(mrb_state *mrb, mrb_value data) -{ - struct ary_subtract_ctx *ctx = (struct ary_subtract_ctx *)mrb_cptr(data); - ary_destroy_temp_set(mrb, ctx->set); - return mrb_nil_value(); -} - static mrb_value ary_subtract_internal(mrb_state *mrb, mrb_value self, mrb_int argc, const mrb_value *argv) { @@ -502,8 +494,9 @@ ary_subtract_internal(mrb_state *mrb, mrb_value self, mrb_int argc, const mrb_va ary_init_temp_set(mrb, set, total_len); struct ary_subtract_ctx ctx = { set, self, result, argv_copies, argc }; - mrb_ensure(mrb, ary_subtract_body, mrb_cptr_value(mrb, &ctx), - ary_subtract_ensure, mrb_cptr_value(mrb, &ctx)); + MRB_ENSURE(mrb, result, ary_subtract_body, &ctx) { + ary_destroy_temp_set(mrb, set); + } } else { mrb_int self_len = RARRAY_LEN(self); @@ -588,9 +581,9 @@ struct ary_union_ctx { }; static mrb_value -ary_union_body(mrb_state *mrb, mrb_value data) +ary_union_body(mrb_state *mrb, void *data) { - struct ary_union_ctx *ctx = (struct ary_union_ctx *)mrb_cptr(data); + struct ary_union_ctx *ctx = (struct ary_union_ctx *)data; /* Add unique elements from self */ for (mrb_int i = 0; i < RARRAY_LEN(ctx->self_copy); i++) { @@ -618,14 +611,6 @@ ary_union_body(mrb_state *mrb, mrb_value data) return ctx->result; } -static mrb_value -ary_union_ensure(mrb_state *mrb, mrb_value data) -{ - struct ary_union_ctx *ctx = (struct ary_union_ctx *)mrb_cptr(data); - ary_destroy_temp_set(mrb, ctx->set); - return mrb_nil_value(); -} - static mrb_value ary_union_internal(mrb_state *mrb, mrb_value self, mrb_int argc, const mrb_value *argv) { @@ -646,8 +631,9 @@ ary_union_internal(mrb_state *mrb, mrb_value self, mrb_int argc, const mrb_value ary_init_temp_set(mrb, set, total_len); struct ary_union_ctx ctx = { set, self_copy, result, argv_copies, argc }; - mrb_ensure(mrb, ary_union_body, mrb_cptr_value(mrb, &ctx), - ary_union_ensure, mrb_cptr_value(mrb, &ctx)); + MRB_ENSURE(mrb, result, ary_union_body, &ctx) { + ary_destroy_temp_set(mrb, set); + } } else { /* Use linear search for small arrays */ @@ -717,9 +703,9 @@ struct ary_intersection_ctx { }; static mrb_value -ary_intersection_body(mrb_state *mrb, mrb_value data) +ary_intersection_body(mrb_state *mrb, void *data) { - struct ary_intersection_ctx *ctx = (struct ary_intersection_ctx *)mrb_cptr(data); + struct ary_intersection_ctx *ctx = (struct ary_intersection_ctx *)data; for (mrb_int i = 0; i < ctx->argc; i++) { ary_populate_temp_set(mrb, ctx->set, ctx->argv[i]); @@ -737,14 +723,6 @@ ary_intersection_body(mrb_state *mrb, mrb_value data) return ctx->result; } -static mrb_value -ary_intersection_ensure(mrb_state *mrb, mrb_value data) -{ - struct ary_intersection_ctx *ctx = (struct ary_intersection_ctx *)mrb_cptr(data); - ary_destroy_temp_set(mrb, ctx->set); - return mrb_nil_value(); -} - static mrb_value ary_intersection_internal(mrb_state *mrb, mrb_value self, mrb_int argc, const mrb_value *argv) { @@ -768,8 +746,9 @@ ary_intersection_internal(mrb_state *mrb, mrb_value self, mrb_int argc, const mr ary_init_temp_set(mrb, set, total_len); struct ary_intersection_ctx ctx = { set, self, result, argv_copies, argc }; - mrb_ensure(mrb, ary_intersection_body, mrb_cptr_value(mrb, &ctx), - ary_intersection_ensure, mrb_cptr_value(mrb, &ctx)); + MRB_ENSURE(mrb, result, ary_intersection_body, &ctx) { + ary_destroy_temp_set(mrb, set); + } } else { mrb_int self_len = RARRAY_LEN(self); @@ -869,9 +848,9 @@ struct ary_intersect_p_ctx { }; static mrb_value -ary_intersect_p_body(mrb_state *mrb, mrb_value data) +ary_intersect_p_body(mrb_state *mrb, void *data) { - struct ary_intersect_p_ctx *ctx = (struct ary_intersect_p_ctx *)mrb_cptr(data); + struct ary_intersect_p_ctx *ctx = (struct ary_intersect_p_ctx *)data; ary_populate_temp_set(mrb, ctx->set, ctx->shorter_ary_copy); @@ -886,14 +865,6 @@ ary_intersect_p_body(mrb_state *mrb, mrb_value data) return mrb_nil_value(); } -static mrb_value -ary_intersect_p_ensure(mrb_state *mrb, mrb_value data) -{ - struct ary_intersect_p_ctx *ctx = (struct ary_intersect_p_ctx *)mrb_cptr(data); - ary_destroy_temp_set(mrb, ctx->set); - return mrb_nil_value(); -} - static mrb_value ary_intersect_p(mrb_state *mrb, mrb_value self) { @@ -924,8 +895,10 @@ ary_intersect_p(mrb_state *mrb, mrb_value self) mrb_bool found = FALSE; struct ary_intersect_p_ctx ctx = { set, shorter_ary_copy, longer_ary, &found }; - mrb_ensure(mrb, ary_intersect_p_body, mrb_cptr_value(mrb, &ctx), - ary_intersect_p_ensure, mrb_cptr_value(mrb, &ctx)); + mrb_value result; + MRB_ENSURE(mrb, result, ary_intersect_p_body, &ctx) { + ary_destroy_temp_set(mrb, set); + } if (found) { return mrb_true_value(); @@ -1089,9 +1062,9 @@ struct ary_uniq_bang_ctx { }; static mrb_value -ary_uniq_bang_body(mrb_state *mrb, mrb_value data) +ary_uniq_bang_body(mrb_state *mrb, void *data) { - struct ary_uniq_bang_ctx *ctx = (struct ary_uniq_bang_ctx *)mrb_cptr(data); + struct ary_uniq_bang_ctx *ctx = (struct ary_uniq_bang_ctx *)data; ary_populate_temp_set(mrb, ctx->set, ctx->self_copy); @@ -1110,14 +1083,6 @@ ary_uniq_bang_body(mrb_state *mrb, mrb_value data) return mrb_nil_value(); } -static mrb_value -ary_uniq_bang_ensure(mrb_state *mrb, mrb_value data) -{ - struct ary_uniq_bang_ctx *ctx = (struct ary_uniq_bang_ctx *)mrb_cptr(data); - ary_destroy_temp_set(mrb, ctx->set); - return mrb_nil_value(); -} - static mrb_value ary_uniq_bang(mrb_state *mrb, mrb_value self) { @@ -1139,8 +1104,10 @@ ary_uniq_bang(mrb_state *mrb, mrb_value self) ary_init_temp_set(mrb, set, len); struct ary_uniq_bang_ctx ctx = { set, self_copy, self, &write_pos, len }; - mrb_ensure(mrb, ary_uniq_bang_body, mrb_cptr_value(mrb, &ctx), - ary_uniq_bang_ensure, mrb_cptr_value(mrb, &ctx)); + mrb_value result; + MRB_ENSURE(mrb, result, ary_uniq_bang_body, &ctx) { + ary_destroy_temp_set(mrb, set); + } } else { for (mrb_int read_pos = 0; read_pos < len; read_pos++) { From 5bc08befaea7a44aa230f24a97e3a09f3becc6aa Mon Sep 17 00:00:00 2001 From: dearblue Date: Sat, 24 Jan 2026 11:34:02 +0900 Subject: [PATCH 3/3] Use the specialized `MRB_ENSURE()` instead of `mrb_protect_error()` --- mrbgems/mruby-bigint/core/bigint.c | 35 +++++++++++------------------- mrbgems/mruby-eval/src/eval.c | 10 ++++----- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/mrbgems/mruby-bigint/core/bigint.c b/mrbgems/mruby-bigint/core/bigint.c index 1538acedb..af85a5ddc 100644 --- a/mrbgems/mruby-bigint/core/bigint.c +++ b/mrbgems/mruby-bigint/core/bigint.c @@ -2127,16 +2127,11 @@ static void mpz_mul_all_ones(mpz_ctx_t *ctx, mpz_t *w, size_t n, size_t m) { struct mpz_mul_all_ones_data d = {ctx, w, n, m, {0,0,0}, {0,0,0}}; - mrb_bool error = FALSE; - - mrb_value exc = mrb_protect_error(MPZ_MRB(ctx), mpz_mul_all_ones_body, &d, &error); - - /* Cleanup always runs (mpz_clear is safe on zero-initialized mpz_t) */ - mpz_clear(ctx, &d.a); - mpz_clear(ctx, &d.b); - - if (error) { - mrb_exc_raise(MPZ_MRB(ctx), exc); + mrb_value exc; + MRB_ENSURE(MPZ_MRB(ctx), exc, mpz_mul_all_ones_body, &d) { + /* Cleanup always runs (mpz_clear is safe on zero-initialized mpz_t) */ + mpz_clear(ctx, &d.a); + mpz_clear(ctx, &d.b); } } @@ -3731,18 +3726,14 @@ mpz_get_str_dc(mpz_ctx_t *ctx, char *s, mpz_t *x) d.num_digits = num_digits; d.num_powers = 0; - mrb_bool error = FALSE; - mrb_value exc = mrb_protect_error(MPZ_MRB(ctx), mpz_get_str_dc_body, &d, &error); - - /* Cleanup always runs (mpz_clear is safe on zero-initialized mpz_t) */ - for (size_t i = 0; i < d.num_powers; i++) { - mpz_clear(ctx, &d.pow5[i]); - } - mpz_clear(ctx, &d.tmp); - dc_scratch_clear(ctx, &d.scratch); - - if (error) { - mrb_exc_raise(MPZ_MRB(ctx), exc); + mrb_value exc; + MRB_ENSURE(MPZ_MRB(ctx), exc, mpz_get_str_dc_body, &d) { + /* Cleanup always runs (mpz_clear is safe on zero-initialized mpz_t) */ + for (size_t i = 0; i < d.num_powers; i++) { + mpz_clear(ctx, &d.pow5[i]); + } + mpz_clear(ctx, &d.tmp); + dc_scratch_clear(ctx, &d.scratch); } /* Remove leading zeros (but keep at least one digit) */ diff --git a/mrbgems/mruby-eval/src/eval.c b/mrbgems/mruby-eval/src/eval.c index 4784f0489..8845a534e 100644 --- a/mrbgems/mruby-eval/src/eval.c +++ b/mrbgems/mruby-eval/src/eval.c @@ -252,11 +252,11 @@ binding_eval_prepare(mrb_state *mrb, mrb_value binding, const char *expr, mrb_in d.cxt->upper = proc; d.pstate = mrb_parse_nstring(mrb, expr, exprlen, d.cxt); - mrb_bool error; - mrb_value ret = mrb_protect_error(mrb, binding_eval_prepare_body, &d, &error); - if (d.pstate) mrb_parser_free(d.pstate); - if (d.cxt) mrb_ccontext_free(mrb, d.cxt); - if (error) mrb_exc_raise(mrb, ret); + mrb_value ret; + MRB_ENSURE(mrb, ret, binding_eval_prepare_body, &d) { + if (d.pstate) mrb_parser_free(d.pstate); + if (d.cxt) mrb_ccontext_free(mrb, d.cxt); + } } /*