mruby-exit.c: make exit() to raise SystemExit exception.

Now it allows error handlers to work, unlike `exit!`.
This commit is contained in:
Yukihiro "Matz" Matsumoto
2022-09-21 22:33:32 +09:00
parent 11504318b8
commit 07470eb538
4 changed files with 61 additions and 24 deletions
+8
View File
@@ -21,6 +21,14 @@ struct RException {
struct RObject *backtrace; // NULL, RArray or RData
};
/* error that should terminate execution */
#define MRB_EXC_EXIT 65536
#define MRB_EXC_EXIT_P(e) ((e)->flags & MRB_EXC_EXIT)
/* retrieve status value from exc; need <mruby/variable.h> and <mruby/presym.h> */
#define MRB_EXC_EXIT_STATUS(mrb,e) ((int)mrb_as_int((mrb),mrb_obj_iv_get((mrb),(e),MRB_SYM(status))))
/* exit with SystemExit status */
#define MRB_EXC_CHECK_EXIT(mrb,e) do {if (MRB_EXC_EXIT_P(e)) exit(MRB_EXC_EXIT_STATUS((mrb),(e)));} while (0)
#define mrb_exc_ptr(v) ((struct RException*)mrb_ptr(v))
MRB_API mrb_noreturn void mrb_sys_fail(mrb_state *mrb, const char *mesg);
+2
View File
@@ -18,6 +18,7 @@
#include <mruby/dump.h>
#include <mruby/string.h>
#include <mruby/variable.h>
#include <mruby/error.h>
#include <mruby/presym.h>
#include <stdlib.h>
@@ -674,6 +675,7 @@ main(int argc, char **argv)
stack_keep = proc->body.irep->nlocals;
/* did an exception occur? */
if (mrb->exc) {
MRB_EXC_CHECK_EXIT(mrb, mrb->exc);
p(mrb, mrb_obj_value(mrb->exc), 0);
mrb->exc = 0;
}
@@ -11,6 +11,8 @@
#include <mruby/dump.h>
#include <mruby/variable.h>
#include <mruby/proc.h>
#include <mruby/error.h>
#include <mruby/presym.h>
#if defined(_WIN32) || defined(_WIN64)
# include <io.h> /* for setmode */
@@ -366,6 +368,7 @@ main(int argc, char **argv)
mrb_gc_arena_restore(mrb, ai);
mrbc_context_free(mrb, c);
if (mrb->exc) {
MRB_EXC_CHECK_EXIT(mrb, mrb->exc);
if (!mrb_undef_p(v)) {
mrb_print_error(mrb);
}
+48 -24
View File
@@ -1,5 +1,8 @@
#include <stdlib.h>
#include <mruby.h>
#include <mruby/error.h>
#include <mruby/variable.h>
#include <mruby/presym.h>
#ifndef EXIT_SUCCESS
# define EXIT_SUCCESS 0
@@ -9,48 +12,69 @@
# define EXIT_FAILURE 1
#endif
/*
* call-seq:
* exit(status=false)
*
* Exits the process immediately. No exit handlers are run currently.
* <em>status</em> is returned to the underlying system as the
* exit status.
*
* exit(true)
*/
static int
get_status(mrb_state *mrb)
{
mrb_value status = mrb_true_value();
mrb_get_args(mrb, "|o", &status);
if (mrb_true_p(status)) return EXIT_SUCCESS;
if (mrb_false_p(status)) return EXIT_FAILURE;
return (int)mrb_as_int(mrb, status);
}
/*
* call-seq:
* exit!(status=false)
* exit(status=true)
*
* Initiates the termination of the Ruby script by raising the
* SystemExit exception. This exception may be caught. The
* optional parameter is used to return a status code to the invoking
* environment.
*
* +true+ and +false+ of _status_ means success and failure
* respectively. The interpretation of other integer values are
* system dependent.
*
* exit(0)
*/
static mrb_value
f_exit(mrb_state *mrb, mrb_value self)
{
int status = get_status(mrb);
mrb_value exc = mrb_obj_new(mrb, mrb_exc_get_id(mrb, MRB_SYM(SystemExit)), 0, NULL);
struct RException *e = mrb_exc_ptr(exc);
e->flags |= MRB_EXC_EXIT;
mrb_iv_set(mrb, exc, MRB_SYM(status), mrb_int_value(mrb, (mrb_int)status));
mrb_exc_raise(mrb, exc);
/* not reached */
return mrb_nil_value();
}
/*
* call-seq:
* exit!(status=true)
*
* Exits the process immediately. No exit handlers are run.
* <em>status</em> is returned to the underlying system as the
* exit status.
*
* exit!(true)
* exit!(0)
*/
static mrb_value
f_exit_bang(mrb_state *mrb, mrb_value self)
{
mrb_value status = mrb_true_value();
int istatus;
mrb_get_args(mrb, "|o", &status);
istatus = mrb_true_p(status) ? EXIT_SUCCESS :
mrb_false_p(status) ? EXIT_FAILURE :
(int)mrb_int(mrb, status);
exit(istatus);
exit(get_status(mrb));
/* not reached */
return status;
return mrb_nil_value();
}
void
mrb_mruby_exit_gem_init(mrb_state* mrb)
{
mrb_define_method(mrb, mrb->kernel_module, "exit", f_exit_bang, MRB_ARGS_OPT(1));
mrb_define_method(mrb, mrb->kernel_module, "exit!", f_exit_bang, MRB_ARGS_OPT(1));
mrb_define_class_id(mrb, MRB_SYM(SystemExit), mrb->eException_class);
mrb_define_method_id(mrb, mrb->kernel_module, MRB_SYM(exit), f_exit, MRB_ARGS_OPT(1));
mrb_define_method_id(mrb, mrb->kernel_module, MRB_SYM_B(exit), f_exit_bang, MRB_ARGS_OPT(1));
}
void