summaryrefslogtreecommitdiff
path: root/src/error.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/error.c')
-rw-r--r--src/error.c321
1 files changed, 268 insertions, 53 deletions
diff --git a/src/error.c b/src/error.c
index 6199cdf..cce3825 100644
--- a/src/error.c
+++ b/src/error.c
@@ -16,7 +16,6 @@
#include <mruby/class.h>
#include <mruby/throw.h>
#include <mruby/internal.h>
-#include <mruby/presym.h>
void
mrb_exc_mesg_set(mrb_state *mrb, struct RException *exc, mrb_value mesg)
@@ -80,7 +79,7 @@ exc_initialize(mrb_state *mrb, mrb_value exc)
* With no argument, or if the argument is the same as the receiver,
* return the receiver. Otherwise, create a new
* exception object of the same class as the receiver, but with a
- * message equal to <code>string</code>.
+ * message equal to `string`.
*
*/
@@ -126,9 +125,9 @@ exc_to_s(mrb_state *mrb, mrb_value exc)
* call-seq:
* exception.inspect -> string
*
- * Returns this exception's file name, line number,
+ * Returns this exception's filename, line number,
* message and class name.
- * If file name or line number is not set,
+ * If filename or line number is not set,
* returns message and class name.
*/
@@ -137,6 +136,14 @@ mrb_exc_inspect(mrb_state *mrb, mrb_value exc)
{
mrb_value cname = mrb_mod_to_s(mrb, mrb_obj_value(mrb_obj_class(mrb, exc)));
mrb_value mesg = mrb_exc_mesg_get(mrb, mrb_exc_ptr(exc)); /* string or nil */
+ return (mrb_nil_p(mesg)||RSTRING_LEN(mesg)==0) ? cname : mrb_format(mrb, "#<%v: %v>", cname, mesg);
+}
+
+mrb_value
+mrb_exc_get_output(mrb_state *mrb, struct RObject *exc)
+{
+ mrb_value cname = mrb_mod_to_s(mrb, mrb_obj_value(mrb_class_real(exc->c)));
+ mrb_value mesg = mrb_exc_mesg_get(mrb, (struct RException*)exc); /* string or nil */
return (mrb_nil_p(mesg)||RSTRING_LEN(mesg)==0) ? cname : mrb_format(mrb, "%v (%v)", mesg, cname);
}
@@ -199,6 +206,19 @@ exc_throw(mrb_state *mrb, mrb_value exc)
MRB_THROW(mrb->jmp);
}
+/*
+ * Raises the given exception object.
+ *
+ * This function sets the provided exception object as the current
+ * exception in the mruby state and then triggers the exception
+ * handling mechanism (longjmp).
+ *
+ * If the provided object is a 'break' object, it's handled specially.
+ * If it's not an exception object, a TypeError is raised.
+ *
+ * mrb: The mruby state.
+ * exc: The exception object to raise.
+ */
MRB_API mrb_noreturn void
mrb_exc_raise(mrb_state *mrb, mrb_value exc)
{
@@ -214,6 +234,16 @@ mrb_exc_raise(mrb_state *mrb, mrb_value exc)
exc_throw(mrb, exc);
}
+/*
+ * Creates a new exception of class `c` with the message `msg` and raises it.
+ *
+ * This is a convenience function that combines creating an exception
+ * from a C string and then raising it.
+ *
+ * mrb: The mruby state.
+ * c: The exception class to instantiate.
+ * msg: The C string message for the exception.
+ */
MRB_API mrb_noreturn void
mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg)
{
@@ -221,42 +251,41 @@ mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg)
}
/*
- * <code>vsprintf</code> like formatting.
+ * Formats arguments according to a format string, similar to vsprintf.
+ * This function is the core of mruby's string formatting capabilities.
+ * It takes a format string and a va_list of arguments and returns a
+ * new mruby string with the formatted result.
*
- * The syntax of a format sequence is as follows.
+ * The format string supports various specifiers to control how arguments
+ * are converted to strings.
*
+ * Format Sequence Syntax:
* %[modifier]specifier
*
- * The modifiers are:
- *
- * ----------+------------------------------------------------------------
- * Modifier | Meaning
- * ----------+------------------------------------------------------------
- * ! | Convert to string by corresponding `inspect` instead of
- * | corresponding `to_s`.
- * ----------+------------------------------------------------------------
- *
- * The specifiers are:
- *
- * ----------+----------------+--------------------------------------------
- * Specifier | Argument Type | Note
- * ----------+----------------+--------------------------------------------
- * c | char |
- * d | int |
- * f | mrb_float |
- * i | mrb_int |
- * l | char*, size_t | Arguments are string and length.
- * n | mrb_sym |
- * s | char* | Argument is NUL terminated string.
- * t | mrb_value | Convert to type (class) of object.
- * v,S | mrb_value |
- * C | struct RClass* |
- * T | mrb_value | Convert to real type (class) of object.
- * Y | mrb_value | Same as `!v` if argument is `true`, `false`
- * | | or `nil`, otherwise same as `T`.
- * % | - | Convert to percent sign itself (no argument
- * | | taken).
- * ----------+----------------+--------------------------------------------
+ * Modifier:
+ * ! : Use the 'inspect' method for conversion instead of 'to_s'.
+ *
+ * Specifiers:
+ * c : char
+ * d : int (decimal)
+ * i : mrb_int (decimal)
+ * f : mrb_float
+ * l : char* and size_t (string with length)
+ * n : mrb_sym (symbol name)
+ * s : char* (NUL-terminated C string)
+ * t : mrb_value (type/class of the object)
+ * v,S: mrb_value (converted using to_s or inspect based on '!')
+ * C : struct RClass* (class name)
+ * T : mrb_value (real type/class of the object)
+ * Y : mrb_value (uses 'inspect' if true, false, or nil, otherwise same as 'T')
+ * % : Literal '%' character (no argument consumed)
+ *
+ * mrb: The mruby state.
+ * format: The format string.
+ * ap: The va_list of arguments.
+ *
+ * Returns a new mrb_value string containing the formatted output.
+ * Raises ArgumentError if the format string is malformed.
*/
MRB_API mrb_value
mrb_vformat(mrb_state *mrb, const char *format, va_list ap)
@@ -321,6 +350,7 @@ mrb_vformat(mrb_state *mrb, const char *format, va_list ap)
goto L_cat_obj;
case 's':
chars = va_arg(ap, char*);
+ if (chars == NULL) chars = "(null)";
len = strlen(chars);
goto L_cat;
case 't':
@@ -378,6 +408,19 @@ mrb_vformat(mrb_state *mrb, const char *format, va_list ap)
return result;
}
+/*
+ * Formats arguments according to a format string, similar to sprintf.
+ *
+ * This function takes a format string and a variable number of arguments,
+ * then calls mrb_vformat to perform the actual formatting.
+ * See mrb_vformat for details on the format string specifiers.
+ *
+ * mrb: The mruby state.
+ * format: The format string.
+ * ...: Variable arguments to be formatted.
+ *
+ * Returns a new mrb_value string containing the formatted output.
+ */
MRB_API mrb_value
mrb_format(mrb_state *mrb, const char *format, ...)
{
@@ -397,6 +440,19 @@ error_va(mrb_state *mrb, struct RClass *c, const char *fmt, va_list ap)
return mrb_exc_new_str(mrb, c, mrb_vformat(mrb, fmt, ap));
}
+/*
+ * Creates a new exception of class `c` with a formatted message and raises it.
+ *
+ * This function formats a message string using `fmt` and the subsequent
+ * variable arguments, then creates an exception of class `c` with this
+ * message, and finally raises the exception.
+ * See mrb_vformat for details on the format string specifiers.
+ *
+ * mrb: The mruby state.
+ * c: The exception class to instantiate.
+ * fmt: The format string for the exception message.
+ * ...: Variable arguments for the format string.
+ */
MRB_API mrb_noreturn void
mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...)
{
@@ -410,6 +466,20 @@ mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...)
mrb_exc_raise(mrb, exc);
}
+/*
+ * Raises a NameError exception with a formatted message.
+ *
+ * This function creates a NameError exception. The message is generated
+ * from `fmt` and the variable arguments. The symbol `id` (e.g., the name
+ * of a missing constant or variable) is associated with the exception object
+ * via an instance variable named 'name'.
+ * See mrb_vformat for details on the format string specifiers.
+ *
+ * mrb: The mruby state.
+ * id: The symbol representing the name that caused the error.
+ * fmt: The format string for the exception message.
+ * ...: Variable arguments for the format string.
+ */
MRB_API mrb_noreturn void
mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...)
{
@@ -423,6 +493,18 @@ mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...)
mrb_exc_raise(mrb, exc);
}
+/*
+ * Prints a warning message to stderr.
+ *
+ * The message is formatted using `fmt` and the subsequent variable arguments.
+ * The output is prefixed with "warning: " and followed by a newline.
+ * This function does nothing if MRB_NO_STDIO is defined.
+ * See mrb_vformat for details on the format string specifiers.
+ *
+ * mrb: The mruby state.
+ * fmt: The format string for the warning message.
+ * ...: Variable arguments for the format string.
+ */
MRB_API void
mrb_warn(mrb_state *mrb, const char *fmt, ...)
{
@@ -439,6 +521,17 @@ mrb_warn(mrb_state *mrb, const char *fmt, ...)
#endif
}
+/*
+ * Reports an internal mruby bug, prints a message to stderr, and terminates the program.
+ *
+ * This function is called when an unexpected internal error occurs within mruby.
+ * It prints the given message prefixed with "bug: " to stderr and then
+ * calls exit(EXIT_FAILURE).
+ * If MRB_NO_STDIO is defined, the message is not printed, but the program still exits.
+ *
+ * mrb: The mruby state (currently unused in the function body but part of the API).
+ * mesg: The C string message describing the bug.
+ */
MRB_API mrb_noreturn void
mrb_bug(mrb_state *mrb, const char *mesg)
{
@@ -476,6 +569,26 @@ mrb_make_exception(mrb_state *mrb, mrb_value exc, mrb_value mesg)
return exc;
}
+/*
+ * Raises a SystemCallError if available, otherwise a RuntimeError,
+ * based on the current `errno` value.
+ *
+ * If the SystemCallError class is defined, this function attempts to call
+ * its `_sys_fail` method with the current `errno` and an optional
+ * message. This typically results in a SystemCallError being raised.
+ *
+ * If SystemCallError is not defined, or if the call to `_sys_fail`
+ * itself fails (which shouldn't happen in normal circumstances but leads
+ * to mrb_raise), it falls back to raising a RuntimeError with the
+ * given message (or a default message if `mesg` is NULL, though the
+ * current implementation would pass NULL to mrb_raise which might be
+ * an issue).
+ *
+ * mrb: The mruby state.
+ * mesg: An optional C string message to append to the error. If NULL,
+ * a default message or no message might be used depending on the
+ * error path.
+ */
MRB_API mrb_noreturn void
mrb_sys_fail(mrb_state *mrb, const char *mesg)
{
@@ -493,6 +606,22 @@ mrb_sys_fail(mrb_state *mrb, const char *mesg)
mrb_raise(mrb, E_RUNTIME_ERROR, mesg);
}
+/*
+ * Raises a NoMethodError exception with a formatted message.
+ *
+ * This function creates a NoMethodError. The message is generated from
+ * `fmt` and the variable arguments. The symbol `id` (the name of the
+ * missing method) and `args` (the arguments passed to the method)
+ * are associated with the exception object via instance variables
+ * named 'name' and 'args', respectively.
+ * See mrb_vformat for details on the format string specifiers.
+ *
+ * mrb: The mruby state.
+ * id: The symbol representing the name of the undefined method.
+ * args: The arguments that were passed to the method call.
+ * fmt: The format string for the exception message.
+ * ...: Variable arguments for the format string.
+ */
MRB_API mrb_noreturn void
mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, ...)
{
@@ -513,12 +642,31 @@ frozen_error(mrb_state *mrb, mrb_value v)
mrb_raisef(mrb, E_FROZEN_ERROR, "can't modify frozen %T", v);
}
+/*
+ * Raises a FrozenError for the given frozen object.
+ *
+ * This function is called when an attempt is made to modify an object
+ * that has been frozen. It constructs and raises a FrozenError,
+ * indicating the specific object that could not be modified.
+ *
+ * mrb: The mruby state.
+ * frozen_obj: A pointer to the RBasic structure of the frozen object.
+ */
MRB_API mrb_noreturn void
mrb_frozen_error(mrb_state *mrb, void *frozen_obj)
{
frozen_error(mrb, mrb_obj_value(frozen_obj));
}
+/*
+ * Checks if the given object is frozen. If it is, raises a FrozenError.
+ *
+ * This utility function is used before attempting an operation that
+ * would modify an object, to ensure that the operation is allowed.
+ *
+ * mrb: The mruby state.
+ * o: A pointer to the RBasic structure of the object to check.
+ */
MRB_API void
mrb_check_frozen(mrb_state *mrb, void *o)
{
@@ -527,6 +675,21 @@ mrb_check_frozen(mrb_state *mrb, void *o)
}
}
+/*
+ * Checks if the given mrb_value refers to a frozen object.
+ * If it is frozen, or if it's an immediate value (which are implicitly
+ * unmodifiable in a way that would trigger a FrozenError for heap objects),
+ * this function raises a FrozenError.
+ *
+ * Note: The check `mrb_immediate_p(v)` combined with `frozen_error`
+ * might be misleading. Immediate values are not "frozen" in the same
+ * sense as heap objects. This function effectively raises a FrozenError
+ * if an attempt is made to modify an immediate value or a
+ * heap-allocated object that is explicitly frozen.
+ *
+ * mrb: The mruby state.
+ * v: The mrb_value to check.
+ */
MRB_API void
mrb_check_frozen_value(mrb_state *mrb, mrb_value v)
{
@@ -535,6 +698,21 @@ mrb_check_frozen_value(mrb_state *mrb, mrb_value v)
}
}
+/*
+ * Raises an ArgumentError indicating a mismatch in the number of arguments.
+ *
+ * This function is used to report errors when a method receives an
+ * incorrect number of arguments. It formats a message specifying the
+ * number of arguments received (`argc`) and the expected number,
+ * which can be an exact number (`min` == `max`), a minimum (`max` < 0),
+ * or a range (`min` to `max`).
+ *
+ * mrb: The mruby state.
+ * argc: The number of arguments actually received.
+ * min: The minimum number of arguments expected.
+ * max: The maximum number of arguments expected. If negative, it means
+ * `min` or more arguments are expected.
+ */
MRB_API mrb_noreturn void
mrb_argnum_error(mrb_state *mrb, mrb_int argc, int min, int max)
{
@@ -562,13 +740,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;
@@ -655,10 +827,28 @@ mrb_raise_nomemory(mrb_state *mrb)
}
}
+/*
+ * Prints the current exception and its backtrace to stderr.
+ *
+ * If an exception is set in the mruby state (`mrb->exc`), this function
+ * attempts to print its details, including the class name, message,
+ * and backtrace.
+ * It takes precautions to handle potential errors during the backtrace
+ * printing itself, especially if called from a context without an active
+ * jump buffer (e.g., top-level error).
+ * This function does nothing if MRB_NO_STDIO is defined.
+ *
+ * mrb: The mruby state.
+ */
MRB_API void
mrb_print_error(mrb_state *mrb)
{
#ifndef MRB_NO_STDIO
+ if (!mrb) {
+ /* mrb_open() returned NULL - allocation failed */
+ fputs("Failed to allocate mrb_state\n", stderr);
+ return;
+ }
if (mrb->jmp == NULL) {
struct mrb_jmpbuf c_jmp;
MRB_TRY(&c_jmp) {
@@ -675,7 +865,14 @@ mrb_print_error(mrb_state *mrb)
#endif
}
-/* clear error status in the mrb_state structure */
+/*
+ * Clears the current exception status in the mruby state.
+ *
+ * After this function is called, `mrb->exc` will be NULL, indicating
+ * that there is no pending exception.
+ *
+ * mrb: The mruby state.
+ */
MRB_API void
mrb_clear_error(mrb_state *mrb)
{
@@ -693,23 +890,41 @@ mrb_check_error(mrb_state *mrb)
return FALSE;
}
+/* ---------------------------*/
+static const mrb_mt_entry exception_rom_entries[] = {
+ MRB_MT_ENTRY(exc_exception, MRB_SYM(exception), MRB_ARGS_OPT(1)),
+ MRB_MT_ENTRY(exc_initialize, MRB_SYM(initialize), MRB_ARGS_OPT(1) | MRB_MT_PRIVATE),
+ MRB_MT_ENTRY(exc_to_s, MRB_SYM(to_s), MRB_ARGS_NONE()),
+ MRB_MT_ENTRY(exc_to_s, MRB_SYM(message), MRB_ARGS_NONE()),
+ MRB_MT_ENTRY(mrb_exc_inspect, MRB_SYM(inspect), MRB_ARGS_NONE()),
+ MRB_MT_ENTRY(mrb_exc_backtrace, MRB_SYM(backtrace), MRB_ARGS_NONE()),
+ MRB_MT_ENTRY(exc_set_backtrace, MRB_SYM(set_backtrace), MRB_ARGS_REQ(1)),
+};
+
void
mrb_init_exception(mrb_state *mrb)
{
struct RClass *exception = mrb->eException_class = mrb_define_class_id(mrb, MRB_SYM(Exception), mrb->object_class); /* 15.2.22 */
MRB_SET_INSTANCE_TT(exception, MRB_TT_EXCEPTION);
mrb_define_class_method_id(mrb, exception, MRB_SYM(exception), mrb_instance_new, MRB_ARGS_OPT(1));
- mrb_define_method_id(mrb, exception, MRB_SYM(exception), exc_exception, MRB_ARGS_OPT(1));
- mrb_define_method_id(mrb, exception, MRB_SYM(initialize), exc_initialize, MRB_ARGS_OPT(1));
- mrb_define_method_id(mrb, exception, MRB_SYM(to_s), exc_to_s, MRB_ARGS_NONE());
- mrb_define_method_id(mrb, exception, MRB_SYM(inspect), mrb_exc_inspect, MRB_ARGS_NONE());
- mrb_define_method_id(mrb, exception, MRB_SYM(backtrace), mrb_exc_backtrace, MRB_ARGS_NONE());
- mrb_define_method_id(mrb, exception, MRB_SYM(set_backtrace), exc_set_backtrace, MRB_ARGS_REQ(1));
+ MRB_MT_INIT_ROM(mrb, exception, exception_rom_entries);
mrb->eStandardError_class = mrb_define_class_id(mrb, MRB_SYM(StandardError), mrb->eException_class); /* 15.2.23 */
- mrb_define_class_id(mrb, MRB_SYM(RuntimeError), E_STANDARD_ERROR); /* 15.2.28 */
- struct RClass *script_error = mrb_define_class_id(mrb, MRB_SYM(ScriptError), exception); /* 15.2.37 */
- mrb_define_class_id(mrb, MRB_SYM(SyntaxError), script_error); /* 15.2.38 */
+ mrb_define_class_id(mrb, MRB_SYM(ArgumentError), E_STANDARD_ERROR); /* 15.2.24 */
+ mrb_define_class_id(mrb, MRB_SYM(LocalJumpError), E_STANDARD_ERROR); /* 15.2.25 */
+ struct RClass *range_error = mrb_define_class_id(mrb, MRB_SYM(RangeError), E_STANDARD_ERROR); /* 15.2.26 */
+ mrb_define_class_id(mrb, MRB_SYM(FloatDomainError), range_error);
+ mrb_define_class_id(mrb, MRB_SYM(RegexpError), E_STANDARD_ERROR); /* 15.2.27 */
+ struct RClass *runtime_error = mrb_define_class_id(mrb, MRB_SYM(RuntimeError), E_STANDARD_ERROR); /* 15.2.28 */
+ mrb_define_class_id(mrb, MRB_SYM(FrozenError), runtime_error);
+ mrb_define_class_id(mrb, MRB_SYM(TypeError), E_STANDARD_ERROR); /* 15.2.29 */
+ mrb_define_class_id(mrb, MRB_SYM(ZeroDivisionError), E_STANDARD_ERROR); /* 15.2.30 */
+ struct RClass *script_error = mrb_define_class_id(mrb, MRB_SYM(ScriptError), exception); /* 15.2.37 */
+ mrb_define_class_id(mrb, MRB_SYM(NotImplementedError), script_error);
+ mrb_define_class_id(mrb, MRB_SYM(SyntaxError), script_error); /* 15.2.38 */
+ struct RClass *index_error = mrb_define_class_id(mrb, MRB_SYM(IndexError), E_STANDARD_ERROR); /* 15.2.33 */
+ mrb_define_class_id(mrb, MRB_SYM(KeyError), index_error);
+ mrb_define_class_id(mrb, MRB_SYM(NoMatchingPatternError), E_STANDARD_ERROR); /* pattern matching */
struct RClass *stack_error = mrb_define_class_id(mrb, MRB_SYM(SystemStackError), exception);
mrb->stack_err = mrb_obj_ptr(mrb_exc_new_lit(mrb, stack_error, "stack level too deep"));