mruby-exit.c: make exit() to raise SystemExit exception.
Now it allows error handlers to work, unlike `exit!`.
MODIFY
include/mruby/error.h
include/mruby/error.h
@@ -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);

MODIFY
mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
#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;
}

MODIFY
mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
mrbgems/mruby-bin-mruby/tools/mruby/mruby.c
#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);
}

MODIFY
mrbgems/mruby-exit/src/mruby-exit.c
mrbgems/mruby-exit/src/mruby-exit.c
#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
# define EXIT_FAILURE 1
#endif
+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)
*
- * Exits the process immediately. No exit handlers are run currently.
- * <em>status</em> is returned to the underlying system as the
- * exit status.
+ * 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(true)
+ * 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=false)
+ * 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

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

Now it allows error handlers to work, unlike `exit!`.