From 05ffe0c441910f2011b64ed74e3cfbaad79b1db9 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 13 Nov 2025 19:10:46 +0900 Subject: [PATCH] mrb_open: return mrb_state with exc set on init failure changed mrb_open() and mrb_open_core() to return mrb_state with mrb->exc set (instead of NULL) when initialization fails. this allows callers to programmatically inspect error details, which is essential for embedded systems without stderr. return NULL only for true allocation failure. added MRB_OPEN_SUCCESS(mrb) macro to check initialization success, since mrb != NULL no longer guarantees success. updated all binary tools (mruby, mirb, mrdb, mrbtest) to use new pattern: check MRB_OPEN_SUCCESS, print exception details via mrb_print_error if available, then mrb_close. mrb_core_init_protect now preserves exception in mrb->exc instead of printing and clearing it, giving caller control over error handling. breaking change: callers must use MRB_OPEN_SUCCESS(mrb) or check both mrb != NULL && mrb->exc == NULL. old NULL-only checks will miss initialization failures. Co-authored-by: Claude --- include/mruby.h | 23 ++++++++++++++++++++ mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c | 12 ++++++++-- mrbgems/mruby-bin-mirb/tools/mirb/mirb.c | 12 ++++++++-- mrbgems/mruby-bin-mruby/tools/mruby/mruby.c | 12 ++++++++-- mrbgems/mruby-test/driver.c | 12 ++++++++-- src/error.c | 8 +------ src/state.c | 13 ++++++----- 7 files changed, 71 insertions(+), 21 deletions(-) diff --git a/include/mruby.h b/include/mruby.h index 6331ca8c9..ee23688ff 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -1270,6 +1270,29 @@ MRB_API mrb_state* mrb_open_core(void); MRB_API void mrb_close(mrb_state *mrb); MRB_API void mrb_method_cache_clear(mrb_state *mrb); +/** + * Check if mrb_open() succeeded + * + * @param mrb + * Pointer returned from mrb_open() or mrb_open_core(). + * @return + * Non-zero if initialization succeeded, 0 if failed. + * @note + * mrb_open() may return non-NULL even on failure (with mrb->exc set). + * Use this macro to check for success: + * @code + * mrb_state *mrb = mrb_open(); + * if (!MRB_OPEN_SUCCESS(mrb)) { + * if (mrb) { + * // Inspect mrb->exc for error details + * mrb_close(mrb); + * } + * return EXIT_FAILURE; + * } + * @endcode + */ +#define MRB_OPEN_SUCCESS(mrb) ((mrb) && !(mrb)->exc) + /** * The memory allocation function. You can redefine this function for your own allocator. * diff --git a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c index 0413c0be0..a135b0fa7 100644 --- a/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c +++ b/mrbgems/mruby-bin-debugger/tools/mrdb/mrdb.c @@ -673,8 +673,16 @@ main(int argc, char **argv) l_restart: - if (mrb == NULL) { - fputs("Invalid mrb_state, exiting mruby\n", stderr); + if (!MRB_OPEN_SUCCESS(mrb)) { + if (mrb) { + /* Initialization failed - print exception details */ + mrb_print_error(mrb); + mrb_close(mrb); + } + else { + /* Allocation failed */ + fputs("Failed to allocate mrb_state, exiting mrdb\n", stderr); + } return EXIT_FAILURE; } diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c index 2c003758e..77ab8ce6b 100644 --- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c +++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c @@ -475,8 +475,16 @@ main(int argc, char **argv) /* new interpreter instance */ mrb = mrb_open(); - if (mrb == NULL) { - fputs("Invalid mrb interpreter, exiting mirb\n", stderr); + if (!MRB_OPEN_SUCCESS(mrb)) { + if (mrb) { + /* Initialization failed - print exception details */ + mrb_print_error(mrb); + mrb_close(mrb); + } + else { + /* Allocation failed */ + fputs("Failed to allocate mrb_state, exiting mirb\n", stderr); + } return EXIT_FAILURE; } diff --git a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c index 253410165..38274c399 100644 --- a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c +++ b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c @@ -282,8 +282,16 @@ main(int argc, char **argv) mrb_value ARGV; mrb_value v; - if (mrb == NULL) { - fprintf(stderr, "%s: Invalid mrb_state, exiting mruby\n", *argv); + if (!MRB_OPEN_SUCCESS(mrb)) { + if (mrb) { + /* Initialization failed - print exception details */ + mrb_print_error(mrb); + mrb_close(mrb); + } + else { + /* Allocation failed */ + fprintf(stderr, "%s: Failed to allocate mrb_state, exiting mruby\n", *argv); + } return EXIT_FAILURE; } diff --git a/mrbgems/mruby-test/driver.c b/mrbgems/mruby-test/driver.c index e62bc0147..d1decad8b 100644 --- a/mrbgems/mruby-test/driver.c +++ b/mrbgems/mruby-test/driver.c @@ -290,8 +290,16 @@ main(int argc, char **argv) /* new interpreter instance */ mrb = mrb_open(); - if (mrb == NULL) { - fputs("Invalid mrb_state, exiting test driver", stderr); + if (!MRB_OPEN_SUCCESS(mrb)) { + if (mrb) { + /* Initialization failed - print exception details */ + mrb_print_error(mrb); + mrb_close(mrb); + } + else { + /* Allocation failed */ + fputs("Failed to allocate mrb_state, exiting test driver", stderr); + } return EXIT_FAILURE; } diff --git a/src/error.c b/src/error.c index 4b39381dd..ffb9e4792 100644 --- a/src/error.c +++ b/src/error.c @@ -741,13 +741,7 @@ mrb_core_init_protect(mrb_state *mrb, void (*body)(mrb_state*, void*), void *opa body(mrb, opaque); err = 0; } MRB_CATCH(&c_jmp) { - if (mrb->exc) { - mrb_print_error(mrb); - mrb->exc = NULL; - } - else { - mrb_core_init_printabort(mrb); - } + /* Leave mrb->exc set for caller to inspect */ } MRB_END_EXC(&c_jmp); mrb->jmp = prev_jmp; diff --git a/src/state.c b/src/state.c index 1b3d687b9..ee536901a 100644 --- a/src/state.c +++ b/src/state.c @@ -50,8 +50,8 @@ mrb_open_core(void) mrb->bootstrapping = TRUE; if (mrb_core_init_protect(mrb, init_gc_and_core, NULL)) { - mrb_close(mrb); - return NULL; + /* Return mrb with mrb->exc set for caller to inspect */ + return mrb; } mrb_method_cache_clear(mrb); @@ -74,14 +74,15 @@ mrb_open(void) { mrb_state *mrb = mrb_open_core(); - if (mrb == NULL) { - return NULL; + if (mrb == NULL || mrb->exc) { + /* Either allocation failed or core init failed */ + return mrb; } #ifndef MRB_NO_GEMS if (mrb_core_init_protect(mrb, init_mrbgems, NULL)) { - mrb_close(mrb); - return NULL; + /* Gem init failed - return mrb with mrb->exc set */ + return mrb; } mrb_gc_arena_restore(mrb, 0); #endif