diff options
Diffstat (limited to 'src/error.c')
| -rw-r--r-- | src/error.c | 321 |
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")); |
