summaryrefslogtreecommitdiff
path: root/error.c
diff options
context:
space:
mode:
Diffstat (limited to 'error.c')
-rw-r--r--error.c2305
1 files changed, 1851 insertions, 454 deletions
diff --git a/error.c b/error.c
index 5bfa2216eb..65ad20a27b 100644
--- a/error.c
+++ b/error.c
@@ -9,23 +9,45 @@
**********************************************************************/
-#include "ruby/ruby.h"
-#include "ruby/st.h"
-#include "ruby/encoding.h"
-#include "internal.h"
-#include "vm_core.h"
+#include "ruby/internal/config.h"
-#include <stdio.h>
+#include <errno.h>
#include <stdarg.h>
+#include <stdio.h>
+
#ifdef HAVE_STDLIB_H
-#include <stdlib.h>
+# include <stdlib.h>
#endif
-#include <errno.h>
+
#ifdef HAVE_UNISTD_H
-#include <unistd.h>
+# include <unistd.h>
#endif
-#define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
+#if defined __APPLE__
+# include <AvailabilityMacros.h>
+#endif
+
+#include "internal.h"
+#include "internal/error.h"
+#include "internal/eval.h"
+#include "internal/hash.h"
+#include "internal/io.h"
+#include "internal/load.h"
+#include "internal/object.h"
+#include "internal/symbol.h"
+#include "internal/thread.h"
+#include "internal/variable.h"
+#include "ruby/encoding.h"
+#include "ruby/st.h"
+#include "ruby_assert.h"
+#include "vm_core.h"
+
+#include "builtin.h"
+
+/*!
+ * \addtogroup exception
+ * \{
+ */
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
@@ -39,14 +61,27 @@
#define WEXITSTATUS(status) (status)
#endif
-extern const char ruby_description[];
+VALUE rb_iseqw_local_variables(VALUE iseqval);
+VALUE rb_iseqw_new(const rb_iseq_t *);
+int rb_str_end_with_asciichar(VALUE str, int c);
+
+long rb_backtrace_length_limit = -1;
+VALUE rb_eEAGAIN;
+VALUE rb_eEWOULDBLOCK;
+VALUE rb_eEINPROGRESS;
+static VALUE rb_mWarning;
+static VALUE rb_cWarningBuffer;
+
+static ID id_warn;
+static ID id_category;
+static ID id_deprecated;
+static ID id_experimental;
+static VALUE sym_category;
+static struct {
+ st_table *id2enum, *enum2id;
+} warning_categories;
-#define REPORTBUG_MSG \
- "[NOTE]\n" \
- "You may have encountered a bug in the Ruby interpreter" \
- " or extension libraries.\n" \
- "Bug reports are welcome.\n" \
- "For details: http://www.ruby-lang.org/bugreport.html\n\n" \
+extern const char ruby_description[];
static const char *
rb_strerrno(int err)
@@ -73,265 +108,724 @@ err_position_0(char *buf, long len, const char *file, int line)
}
}
-static int
-err_position(char *buf, long len)
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 5, 0)
+static VALUE
+err_vcatf(VALUE str, const char *pre, const char *file, int line,
+ const char *fmt, va_list args)
{
- return err_position_0(buf, len, rb_sourcefile(), rb_sourceline());
+ if (file) {
+ rb_str_cat2(str, file);
+ if (line) rb_str_catf(str, ":%d", line);
+ rb_str_cat2(str, ": ");
+ }
+ if (pre) rb_str_cat2(str, pre);
+ rb_str_vcatf(str, fmt, args);
+ return str;
}
-static void
-err_snprintf(char *buf, long len, const char *fmt, va_list args)
+VALUE
+rb_syntax_error_append(VALUE exc, VALUE file, int line, int column,
+ rb_encoding *enc, const char *fmt, va_list args)
+{
+ const char *fn = NIL_P(file) ? NULL : RSTRING_PTR(file);
+ if (!exc) {
+ VALUE mesg = rb_enc_str_new(0, 0, enc);
+ err_vcatf(mesg, NULL, fn, line, fmt, args);
+ rb_str_cat2(mesg, "\n");
+ rb_write_error_str(mesg);
+ }
+ else {
+ VALUE mesg;
+ if (NIL_P(exc)) {
+ mesg = rb_enc_str_new(0, 0, enc);
+ exc = rb_class_new_instance(1, &mesg, rb_eSyntaxError);
+ }
+ else {
+ mesg = rb_attr_get(exc, idMesg);
+ if (RSTRING_LEN(mesg) > 0 && *(RSTRING_END(mesg)-1) != '\n')
+ rb_str_cat_cstr(mesg, "\n");
+ }
+ err_vcatf(mesg, NULL, fn, line, fmt, args);
+ }
+
+ return exc;
+}
+
+static unsigned int warning_disabled_categories = (
+ 1U << RB_WARN_CATEGORY_DEPRECATED |
+ 0);
+
+static unsigned int
+rb_warning_category_mask(VALUE category)
{
- long n;
+ return 1U << rb_warning_category_from_name(category);
+}
- n = err_position(buf, len);
- if (len > n) {
- vsnprintf((char*)buf+n, len-n, fmt, args);
+rb_warning_category_t
+rb_warning_category_from_name(VALUE category)
+{
+ st_data_t cat_value;
+ ID cat_id;
+ Check_Type(category, T_SYMBOL);
+ if (!(cat_id = rb_check_id(&category)) ||
+ !st_lookup(warning_categories.id2enum, cat_id, &cat_value)) {
+ rb_raise(rb_eArgError, "unknown category: %"PRIsVALUE, category);
}
+ return (rb_warning_category_t)cat_value;
}
-static void
-compile_snprintf(char *buf, long len, const char *file, int line, const char *fmt, va_list args)
+static VALUE
+rb_warning_category_to_name(rb_warning_category_t category)
+{
+ st_data_t id;
+ if (!st_lookup(warning_categories.enum2id, category, &id)) {
+ rb_raise(rb_eArgError, "invalid category: %d", (int)category);
+ }
+ return id ? ID2SYM(id) : Qnil;
+}
+
+void
+rb_warning_category_update(unsigned int mask, unsigned int bits)
+{
+ warning_disabled_categories &= ~mask;
+ warning_disabled_categories |= mask & ~bits;
+}
+
+MJIT_FUNC_EXPORTED bool
+rb_warning_category_enabled_p(rb_warning_category_t category)
{
- long n;
+ return !(warning_disabled_categories & (1U << category));
+}
- n = err_position_0(buf, len, file, line);
- if (len > n) {
- vsnprintf((char*)buf+n, len-n, fmt, args);
+/*
+ * call-seq:
+ * Warning[category] -> true or false
+ *
+ * Returns the flag to show the warning messages for +category+.
+ * Supported categories are:
+ *
+ * +:deprecated+ :: deprecation warnings
+ * * assignment of non-nil value to <code>$,</code> and <code>$;</code>
+ * * keyword arguments
+ * * proc/lambda without block
+ * etc.
+ *
+ * +:experimental+ :: experimental features
+ * * Pattern matching
+ */
+
+static VALUE
+rb_warning_s_aref(VALUE mod, VALUE category)
+{
+ rb_warning_category_t cat = rb_warning_category_from_name(category);
+ return RBOOL(rb_warning_category_enabled_p(cat));
+}
+
+/*
+ * call-seq:
+ * Warning[category] = flag -> flag
+ *
+ * Sets the warning flags for +category+.
+ * See Warning.[] for the categories.
+ */
+
+static VALUE
+rb_warning_s_aset(VALUE mod, VALUE category, VALUE flag)
+{
+ unsigned int mask = rb_warning_category_mask(category);
+ unsigned int disabled = warning_disabled_categories;
+ if (!RTEST(flag))
+ disabled |= mask;
+ else
+ disabled &= ~mask;
+ warning_disabled_categories = disabled;
+ return flag;
+}
+
+/*
+ * call-seq:
+ * warn(msg, category: nil) -> nil
+ *
+ * Writes warning message +msg+ to $stderr. This method is called by
+ * Ruby for all emitted warnings. A +category+ may be included with
+ * the warning.
+ *
+ * See the documentation of the Warning module for how to customize this.
+ */
+
+static VALUE
+rb_warning_s_warn(int argc, VALUE *argv, VALUE mod)
+{
+ VALUE str;
+ VALUE opt;
+ VALUE category = Qnil;
+
+ rb_scan_args(argc, argv, "1:", &str, &opt);
+ if (!NIL_P(opt)) rb_get_kwargs(opt, &id_category, 0, 1, &category);
+
+ Check_Type(str, T_STRING);
+ rb_must_asciicompat(str);
+ if (!NIL_P(category)) {
+ rb_warning_category_t cat = rb_warning_category_from_name(category);
+ if (!rb_warning_category_enabled_p(cat)) return Qnil;
}
+ rb_write_error_str(str);
+ return Qnil;
}
-static void
-compile_err_append(const char *s, rb_encoding *enc)
-{
- rb_thread_t *th = GET_THREAD();
- VALUE err = th->errinfo;
- rb_block_t *prev_base_block = th->base_block;
- th->base_block = 0;
- /* base_block should be zero while normal Ruby execution */
- /* after this line, any Ruby code *can* run */
-
- if (th->mild_compile_error) {
- if (!RTEST(err)) {
- err = rb_exc_new3(rb_eSyntaxError,
- rb_enc_str_new(s, strlen(s), enc));
- th->errinfo = err;
- }
- else {
- VALUE str = rb_obj_as_string(err);
+/*
+ * Document-module: Warning
+ *
+ * The Warning module contains a single method named #warn, and the
+ * module extends itself, making Warning.warn available.
+ * Warning.warn is called for all warnings issued by Ruby.
+ * By default, warnings are printed to $stderr.
+ *
+ * Changing the behavior of Warning.warn is useful to customize how warnings are
+ * handled by Ruby, for instance by filtering some warnings, and/or outputting
+ * warnings somewhere other than $stderr.
+ *
+ * If you want to change the behavior of Warning.warn you should use
+ * +Warning.extend(MyNewModuleWithWarnMethod)+ and you can use `super`
+ * to get the default behavior of printing the warning to $stderr.
+ *
+ * Example:
+ * module MyWarningFilter
+ * def warn(message, category: nil, **kwargs)
+ * if /some warning I want to ignore/.match?(message)
+ * # ignore
+ * else
+ * super
+ * end
+ * end
+ * end
+ * Warning.extend MyWarningFilter
+ *
+ * You should never redefine Warning#warn (the instance method), as that will
+ * then no longer provide a way to use the default behavior.
+ *
+ * The +warning+ gem provides convenient ways to customize Warning.warn.
+ */
- rb_str_cat2(str, "\n");
- rb_str_cat2(str, s);
- th->errinfo = rb_exc_new3(rb_eSyntaxError, str);
- }
+static VALUE
+rb_warning_warn(VALUE mod, VALUE str)
+{
+ return rb_funcallv(mod, id_warn, 1, &str);
+}
+
+
+static int
+rb_warning_warn_arity(void)
+{
+ const rb_method_entry_t *me = rb_method_entry(rb_singleton_class(rb_mWarning), id_warn);
+ return me ? rb_method_entry_arity(me) : 1;
+}
+
+static VALUE
+rb_warn_category(VALUE str, VALUE category)
+{
+ if (RUBY_DEBUG && !NIL_P(category)) {
+ rb_warning_category_from_name(category);
+ }
+
+ if (rb_warning_warn_arity() == 1) {
+ return rb_warning_warn(rb_mWarning, str);
}
else {
- if (!RTEST(err)) {
- err = rb_exc_new2(rb_eSyntaxError, "compile error");
- th->errinfo = err;
- }
- rb_write_error(s);
- rb_write_error("\n");
+ VALUE args[2];
+ args[0] = str;
+ args[1] = rb_hash_new();
+ rb_hash_aset(args[1], sym_category, category);
+ return rb_funcallv_kw(rb_mWarning, id_warn, 2, args, RB_PASS_KEYWORDS);
}
+}
+
+static void
+rb_write_warning_str(VALUE str)
+{
+ rb_warning_warn(rb_mWarning, str);
+}
+
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 4, 0)
+static VALUE
+warn_vsprintf(rb_encoding *enc, const char *file, int line, const char *fmt, va_list args)
+{
+ VALUE str = rb_enc_str_new(0, 0, enc);
- /* returned to the parser world */
- th->base_block = prev_base_block;
+ err_vcatf(str, "warning: ", file, line, fmt, args);
+ return rb_str_cat2(str, "\n");
}
void
-rb_compile_error_with_enc(const char *file, int line, void *enc, const char *fmt, ...)
+rb_compile_warn(const char *file, int line, const char *fmt, ...)
{
+ VALUE str;
va_list args;
- char buf[BUFSIZ];
+
+ if (NIL_P(ruby_verbose)) return;
va_start(args, fmt);
- compile_snprintf(buf, BUFSIZ, file, line, fmt, args);
+ str = warn_vsprintf(NULL, file, line, fmt, args);
va_end(args);
- compile_err_append(buf, (rb_encoding *)enc);
+ rb_write_warning_str(str);
}
+/* rb_compile_warning() reports only in verbose mode */
void
-rb_compile_error(const char *file, int line, const char *fmt, ...)
+rb_compile_warning(const char *file, int line, const char *fmt, ...)
{
+ VALUE str;
va_list args;
- char buf[BUFSIZ];
+
+ if (!RTEST(ruby_verbose)) return;
va_start(args, fmt);
- compile_snprintf(buf, BUFSIZ, file, line, fmt, args);
+ str = warn_vsprintf(NULL, file, line, fmt, args);
va_end(args);
- compile_err_append(buf, NULL);
+ rb_write_warning_str(str);
}
void
-rb_compile_error_append(const char *fmt, ...)
+rb_category_compile_warn(rb_warning_category_t category, const char *file, int line, const char *fmt, ...)
{
+ VALUE str;
va_list args;
- char buf[BUFSIZ];
+
+ if (NIL_P(ruby_verbose)) return;
va_start(args, fmt);
- vsnprintf(buf, BUFSIZ, fmt, args);
+ str = warn_vsprintf(NULL, file, line, fmt, args);
va_end(args);
- compile_err_append(buf, NULL);
+ rb_warn_category(str, rb_warning_category_to_name(category));
}
-static void
-compile_warn_print(const char *file, int line, const char *fmt, va_list args)
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 0)
+static VALUE
+warning_string(rb_encoding *enc, const char *fmt, va_list args)
{
- char buf[BUFSIZ];
- int len;
-
- compile_snprintf(buf, BUFSIZ, file, line, fmt, args);
- len = (int)strlen(buf);
- buf[len++] = '\n';
- rb_write_error2(buf, len);
+ int line;
+ const char *file = rb_source_location_cstr(&line);
+ return warn_vsprintf(enc, file, line, fmt, args);
}
+#define with_warning_string(mesg, enc, fmt) \
+ VALUE mesg; \
+ va_list args; va_start(args, fmt); \
+ mesg = warning_string(enc, fmt, args); \
+ va_end(args);
+
void
-rb_compile_warn(const char *file, int line, const char *fmt, ...)
+rb_warn(const char *fmt, ...)
{
- char buf[BUFSIZ];
- va_list args;
+ if (!NIL_P(ruby_verbose)) {
+ with_warning_string(mesg, 0, fmt) {
+ rb_write_warning_str(mesg);
+ }
+ }
+}
- if (NIL_P(ruby_verbose)) return;
+void
+rb_category_warn(rb_warning_category_t category, const char *fmt, ...)
+{
+ if (!NIL_P(ruby_verbose)) {
+ with_warning_string(mesg, 0, fmt) {
+ rb_warn_category(mesg, rb_warning_category_to_name(category));
+ }
+ }
+}
- snprintf(buf, BUFSIZ, "warning: %s", fmt);
+void
+rb_enc_warn(rb_encoding *enc, const char *fmt, ...)
+{
+ if (!NIL_P(ruby_verbose)) {
+ with_warning_string(mesg, enc, fmt) {
+ rb_write_warning_str(mesg);
+ }
+ }
+}
- va_start(args, fmt);
- compile_warn_print(file, line, buf, args);
- va_end(args);
+/* rb_warning() reports only in verbose mode */
+void
+rb_warning(const char *fmt, ...)
+{
+ if (RTEST(ruby_verbose)) {
+ with_warning_string(mesg, 0, fmt) {
+ rb_write_warning_str(mesg);
+ }
+ }
}
-/* rb_compile_warning() reports only in verbose mode */
+/* rb_category_warning() reports only in verbose mode */
void
-rb_compile_warning(const char *file, int line, const char *fmt, ...)
+rb_category_warning(rb_warning_category_t category, const char *fmt, ...)
{
- char buf[BUFSIZ];
- va_list args;
+ if (RTEST(ruby_verbose)) {
+ with_warning_string(mesg, 0, fmt) {
+ rb_warn_category(mesg, rb_warning_category_to_name(category));
+ }
+ }
+}
- if (!RTEST(ruby_verbose)) return;
+VALUE
+rb_warning_string(const char *fmt, ...)
+{
+ with_warning_string(mesg, 0, fmt) {
+ }
+ return mesg;
+}
- snprintf(buf, BUFSIZ, "warning: %s", fmt);
+#if 0
+void
+rb_enc_warning(rb_encoding *enc, const char *fmt, ...)
+{
+ if (RTEST(ruby_verbose)) {
+ with_warning_string(mesg, enc, fmt) {
+ rb_write_warning_str(mesg);
+ }
+ }
+}
+#endif
- va_start(args, fmt);
- compile_warn_print(file, line, buf, args);
- va_end(args);
+static bool
+deprecation_warning_enabled(void)
+{
+ if (NIL_P(ruby_verbose)) return false;
+ if (!rb_warning_category_enabled_p(RB_WARN_CATEGORY_DEPRECATED)) return false;
+ return true;
}
static void
-warn_print(const char *fmt, va_list args)
+warn_deprecated(VALUE mesg, const char *removal, const char *suggest)
{
- char buf[BUFSIZ];
- int len;
-
- err_snprintf(buf, BUFSIZ, fmt, args);
- len = (int)strlen(buf);
- buf[len++] = '\n';
- rb_write_error2(buf, len);
+ rb_str_set_len(mesg, RSTRING_LEN(mesg) - 1);
+ rb_str_cat_cstr(mesg, " is deprecated");
+ if (removal) {
+ rb_str_catf(mesg, " and will be removed in Ruby %s", removal);
+ }
+ if (suggest) rb_str_catf(mesg, "; use %s instead", suggest);
+ rb_str_cat_cstr(mesg, "\n");
+ rb_warn_category(mesg, ID2SYM(id_deprecated));
}
void
-rb_warn(const char *fmt, ...)
+rb_warn_deprecated(const char *fmt, const char *suggest, ...)
{
- char buf[BUFSIZ];
- va_list args;
+ if (!deprecation_warning_enabled()) return;
- if (NIL_P(ruby_verbose)) return;
-
- snprintf(buf, BUFSIZ, "warning: %s", fmt);
-
- va_start(args, fmt);
- warn_print(buf, args);
+ va_list args;
+ va_start(args, suggest);
+ VALUE mesg = warning_string(0, fmt, args);
va_end(args);
+
+ warn_deprecated(mesg, NULL, suggest);
}
-/* rb_warning() reports only in verbose mode */
void
-rb_warning(const char *fmt, ...)
+rb_warn_deprecated_to_remove(const char *removal, const char *fmt, const char *suggest, ...)
{
- char buf[BUFSIZ];
+ if (!deprecation_warning_enabled()) return;
+
va_list args;
+ va_start(args, suggest);
+ VALUE mesg = warning_string(0, fmt, args);
+ va_end(args);
- if (!RTEST(ruby_verbose)) return;
+ warn_deprecated(mesg, removal, suggest);
+}
- snprintf(buf, BUFSIZ, "warning: %s", fmt);
+static inline int
+end_with_asciichar(VALUE str, int c)
+{
+ return RB_TYPE_P(str, T_STRING) &&
+ rb_str_end_with_asciichar(str, c);
+}
- va_start(args, fmt);
- warn_print(buf, args);
- va_end(args);
+/* :nodoc: */
+static VALUE
+warning_write(int argc, VALUE *argv, VALUE buf)
+{
+ while (argc-- > 0) {
+ rb_str_append(buf, *argv++);
+ }
+ return buf;
}
-/*
- * call-seq:
- * warn(msg, ...) -> nil
- *
- * Displays each of the given messages followed by a record separator on
- * STDERR unless warnings have been disabled (for example with the
- * <code>-W0</code> flag).
- *
- * warn("warning 1", "warning 2")
- *
- * <em>produces:</em>
- *
- * warning 1
- * warning 2
- */
+VALUE rb_ec_backtrace_location_ary(const rb_execution_context_t *ec, long lev, long n, bool skip_internal);
static VALUE
-rb_warn_m(int argc, VALUE *argv, VALUE exc)
+rb_warn_m(rb_execution_context_t *ec, VALUE exc, VALUE msgs, VALUE uplevel, VALUE category)
{
+ VALUE location = Qnil;
+ int argc = RARRAY_LENINT(msgs);
+ const VALUE *argv = RARRAY_CONST_PTR(msgs);
+
if (!NIL_P(ruby_verbose) && argc > 0) {
- rb_io_puts(argc, argv, rb_stderr);
+ VALUE str = argv[0];
+ if (!NIL_P(uplevel)) {
+ long lev = NUM2LONG(uplevel);
+ if (lev < 0) {
+ rb_raise(rb_eArgError, "negative level (%ld)", lev);
+ }
+ location = rb_ec_backtrace_location_ary(ec, lev + 1, 1, TRUE);
+ if (!NIL_P(location)) {
+ location = rb_ary_entry(location, 0);
+ }
+ }
+ if (argc > 1 || !NIL_P(uplevel) || !end_with_asciichar(str, '\n')) {
+ VALUE path;
+ if (NIL_P(uplevel)) {
+ str = rb_str_tmp_new(0);
+ }
+ else if (NIL_P(location) ||
+ NIL_P(path = rb_funcall(location, rb_intern("path"), 0))) {
+ str = rb_str_new_cstr("warning: ");
+ }
+ else {
+ str = rb_sprintf("%s:%ld: warning: ",
+ rb_string_value_ptr(&path),
+ NUM2LONG(rb_funcall(location, rb_intern("lineno"), 0)));
+ }
+ RBASIC_SET_CLASS(str, rb_cWarningBuffer);
+ rb_io_puts(argc, argv, str);
+ RBASIC_SET_CLASS(str, rb_cString);
+ }
+
+ if (!NIL_P(category)) {
+ category = rb_to_symbol_type(category);
+ rb_warning_category_from_name(category);
+ }
+
+ if (exc == rb_mWarning) {
+ rb_must_asciicompat(str);
+ rb_write_error_str(str);
+ }
+ else {
+ rb_warn_category(str, category);
+ }
}
return Qnil;
}
-static void
-report_bug(const char *file, int line, const char *fmt, va_list args)
+#define MAX_BUG_REPORTERS 0x100
+
+static struct bug_reporters {
+ void (*func)(FILE *out, void *data);
+ void *data;
+} bug_reporters[MAX_BUG_REPORTERS];
+
+static int bug_reporters_size;
+
+int
+rb_bug_reporter_add(void (*func)(FILE *, void *), void *data)
+{
+ struct bug_reporters *reporter;
+ if (bug_reporters_size >= MAX_BUG_REPORTERS) {
+ return 0; /* failed to register */
+ }
+ reporter = &bug_reporters[bug_reporters_size++];
+ reporter->func = func;
+ reporter->data = data;
+
+ return 1;
+}
+
+/* SIGSEGV handler might have a very small stack. Thus we need to use it carefully. */
+#define REPORT_BUG_BUFSIZ 256
+static FILE *
+bug_report_file(const char *file, int line)
{
- /* SIGSEGV handler might have a very small stack. Thus we need to use it carefully. */
- char buf[256];
+ char buf[REPORT_BUG_BUFSIZ];
FILE *out = stderr;
- int len = err_position_0(buf, 256, file, line);
+ int len = err_position_0(buf, sizeof(buf), file, line);
if ((ssize_t)fwrite(buf, 1, len, out) == (ssize_t)len ||
(ssize_t)fwrite(buf, 1, len, (out = stdout)) == (ssize_t)len) {
+ return out;
+ }
- fputs("[BUG] ", out);
- vsnprintf(buf, 256, fmt, args);
- fputs(buf, out);
- snprintf(buf, 256, "\n%s\n\n", ruby_description);
- fputs(buf, out);
+ return NULL;
+}
+
+FUNC_MINIMIZED(static void bug_important_message(FILE *out, const char *const msg, size_t len));
+
+static void
+bug_important_message(FILE *out, const char *const msg, size_t len)
+{
+ const char *const endmsg = msg + len;
+ const char *p = msg;
+
+ if (!len) return;
+ if (isatty(fileno(out))) {
+ static const char red[] = "\033[;31;1;7m";
+ static const char green[] = "\033[;32;7m";
+ static const char reset[] = "\033[m";
+ const char *e = strchr(p, '\n');
+ const int w = (int)(e - p);
+ do {
+ int i = (int)(e - p);
+ fputs(*p == ' ' ? green : red, out);
+ fwrite(p, 1, e - p, out);
+ for (; i < w; ++i) fputc(' ', out);
+ fputs(reset, out);
+ fputc('\n', out);
+ } while ((p = e + 1) < endmsg && (e = strchr(p, '\n')) != 0 && e > p + 1);
+ }
+ fwrite(p, 1, endmsg - p, out);
+}
+
+static void
+preface_dump(FILE *out)
+{
+#if defined __APPLE__
+ static const char msg[] = ""
+ "-- Crash Report log information "
+ "--------------------------------------------\n"
+ " See Crash Report log file in one of the following locations:\n"
+# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6
+ " * ~/Library/Logs/CrashReporter\n"
+ " * /Library/Logs/CrashReporter\n"
+# endif
+ " * ~/Library/Logs/DiagnosticReports\n"
+ " * /Library/Logs/DiagnosticReports\n"
+ " for more details.\n"
+ "Don't forget to include the above Crash Report log file in bug reports.\n"
+ "\n";
+ const size_t msglen = sizeof(msg) - 1;
+#else
+ const char *msg = NULL;
+ const size_t msglen = 0;
+#endif
+ bug_important_message(out, msg, msglen);
+}
+
+static void
+postscript_dump(FILE *out)
+{
+#if defined __APPLE__
+ static const char msg[] = ""
+ "[IMPORTANT]"
+ /*" ------------------------------------------------"*/
+ "\n""Don't forget to include the Crash Report log file under\n"
+# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6
+ "CrashReporter or "
+# endif
+ "DiagnosticReports directory in bug reports.\n"
+ /*"------------------------------------------------------------\n"*/
+ "\n";
+ const size_t msglen = sizeof(msg) - 1;
+#else
+ const char *msg = NULL;
+ const size_t msglen = 0;
+#endif
+ bug_important_message(out, msg, msglen);
+}
+
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 0)
+static void
+bug_report_begin_valist(FILE *out, const char *fmt, va_list args)
+{
+ char buf[REPORT_BUG_BUFSIZ];
+
+ fputs("[BUG] ", out);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ fputs(buf, out);
+ snprintf(buf, sizeof(buf), "\n%s\n\n", ruby_description);
+ fputs(buf, out);
+ preface_dump(out);
+}
+
+#define bug_report_begin(out, fmt) do { \
+ va_list args; \
+ va_start(args, fmt); \
+ bug_report_begin_valist(out, fmt, args); \
+ va_end(args); \
+} while (0)
+
+static void
+bug_report_end(FILE *out)
+{
+ /* call additional bug reporters */
+ {
+ int i;
+ for (i=0; i<bug_reporters_size; i++) {
+ struct bug_reporters *reporter = &bug_reporters[i];
+ (*reporter->func)(out, reporter->data);
+ }
+ }
+ postscript_dump(out);
+}
+
+#define report_bug(file, line, fmt, ctx) do { \
+ FILE *out = bug_report_file(file, line); \
+ if (out) { \
+ bug_report_begin(out, fmt); \
+ rb_vm_bugreport(ctx); \
+ bug_report_end(out); \
+ } \
+} while (0) \
+
+#define report_bug_valist(file, line, fmt, ctx, args) do { \
+ FILE *out = bug_report_file(file, line); \
+ if (out) { \
+ bug_report_begin_valist(out, fmt, args); \
+ rb_vm_bugreport(ctx); \
+ bug_report_end(out); \
+ } \
+} while (0) \
+
+NORETURN(static void die(void));
+static void
+die(void)
+{
+#if defined(_WIN32) && defined(RUBY_MSVCRT_VERSION) && RUBY_MSVCRT_VERSION >= 80
+ _set_abort_behavior( 0, _CALL_REPORTFAULT);
+#endif
+ abort();
+}
- rb_vm_bugreport();
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 0)
+void
+rb_bug_without_die(const char *fmt, va_list args)
+{
+ const char *file = NULL;
+ int line = 0;
- fprintf(out, REPORTBUG_MSG);
+ if (GET_EC()) {
+ file = rb_source_location_cstr(&line);
}
+
+ report_bug_valist(file, line, fmt, NULL, args);
}
void
rb_bug(const char *fmt, ...)
{
va_list args;
+ va_start(args, fmt);
+ rb_bug_without_die(fmt, args);
+ va_end(args);
+ die();
+}
+
+void
+rb_bug_for_fatal_signal(ruby_sighandler_t default_sighandler, int sig, const void *ctx, const char *fmt, ...)
+{
const char *file = NULL;
int line = 0;
- if (GET_THREAD()) {
- file = rb_sourcefile();
- line = rb_sourceline();
+ if (GET_EC()) {
+ file = rb_source_location_cstr(&line);
}
- va_start(args, fmt);
- report_bug(file, line, fmt, args);
- va_end(args);
+ report_bug(file, line, fmt, ctx);
-#if defined(_WIN32) && defined(RUBY_MSVCRT_VERSION) && RUBY_MSVCRT_VERSION >= 80
- _set_abort_behavior( 0, _CALL_REPORTFAULT);
-#endif
+ if (default_sighandler) default_sighandler(sig);
- abort();
+ die();
}
+
void
rb_bug_errno(const char *mesg, int errno_arg)
{
@@ -372,21 +866,26 @@ rb_async_bug_errno(const char *mesg, int errno_arg)
}
WRITE_CONST(2, "\n\n");
write_or_abort(2, ruby_description, strlen(ruby_description));
- WRITE_CONST(2, "\n\n");
- WRITE_CONST(2, REPORTBUG_MSG);
abort();
}
void
-rb_compile_bug(const char *file, int line, const char *fmt, ...)
+rb_report_bug_valist(VALUE file, int line, const char *fmt, va_list args)
{
- va_list args;
-
- va_start(args, fmt);
- report_bug(file, line, fmt, args);
- va_end(args);
+ report_bug_valist(RSTRING_PTR(file), line, fmt, NULL, args);
+}
- abort();
+MJIT_FUNC_EXPORTED void
+rb_assert_failure(const char *file, int line, const char *name, const char *expr)
+{
+ FILE *out = stderr;
+ fprintf(out, "Assertion Failed: %s:%d:", file, line);
+ if (name) fprintf(out, "%s:", name);
+ fprintf(out, "%s\n%s\n\n", expr, ruby_description);
+ preface_dump(out);
+ rb_vm_bugreport(NULL);
+ bug_report_end(out);
+ die();
}
static const char builtin_types[][10] = {
@@ -400,7 +899,7 @@ static const char builtin_types[][10] = {
"Array",
"Hash",
"Struct",
- "Bignum",
+ "Integer",
"File",
"Data", /* internal use: wrapped C pointers */
"MatchData", /* data of $~ */
@@ -411,15 +910,14 @@ static const char builtin_types[][10] = {
"true",
"false",
"Symbol", /* :symbol */
- "Fixnum",
- "", /* 0x16 */
+ "Integer",
+ "undef", /* internal use: #undef; should not happen */
"", /* 0x17 */
"", /* 0x18 */
"", /* 0x19 */
- "", /* 0x1a */
- "undef", /* internal use: #undef; should not happen */
- "Node", /* internal use: syntax tree node */
- "iClass", /* internal use: mixed-in module holder */
+ "<Memo>", /* internal use: general memo */
+ "<Node>", /* internal use: syntax tree node */
+ "<iClass>", /* internal use: mixed-in module holder */
};
const char *
@@ -432,6 +930,17 @@ rb_builtin_type_name(int t)
return 0;
}
+static VALUE
+displaying_class_of(VALUE x)
+{
+ switch (x) {
+ case Qfalse: return rb_fstring_cstr("false");
+ case Qnil: return rb_fstring_cstr("nil");
+ case Qtrue: return rb_fstring_cstr("true");
+ default: return rb_obj_class(x);
+ }
+}
+
static const char *
builtin_class_name(VALUE x)
{
@@ -441,7 +950,7 @@ builtin_class_name(VALUE x)
etype = "nil";
}
else if (FIXNUM_P(x)) {
- etype = "Fixnum";
+ etype = "Integer";
}
else if (SYMBOL_P(x)) {
etype = "Symbol";
@@ -453,32 +962,77 @@ builtin_class_name(VALUE x)
etype = "false";
}
else {
+ etype = NULL;
+ }
+ return etype;
+}
+
+const char *
+rb_builtin_class_name(VALUE x)
+{
+ const char *etype = builtin_class_name(x);
+
+ if (!etype) {
etype = rb_obj_classname(x);
}
return etype;
}
+COLDFUNC NORETURN(static void unexpected_type(VALUE, int, int));
+#define UNDEF_LEAKED "undef leaked to the Ruby space"
+
+static void
+unexpected_type(VALUE x, int xt, int t)
+{
+ const char *tname = rb_builtin_type_name(t);
+ VALUE mesg, exc = rb_eFatal;
+
+ if (tname) {
+ mesg = rb_sprintf("wrong argument type %"PRIsVALUE" (expected %s)",
+ displaying_class_of(x), tname);
+ exc = rb_eTypeError;
+ }
+ else if (xt > T_MASK && xt <= 0x3f) {
+ mesg = rb_sprintf("unknown type 0x%x (0x%x given, probably comes"
+ " from extension library for ruby 1.8)", t, xt);
+ }
+ else {
+ mesg = rb_sprintf("unknown type 0x%x (0x%x given)", t, xt);
+ }
+ rb_exc_raise(rb_exc_new_str(exc, mesg));
+}
+
void
rb_check_type(VALUE x, int t)
{
int xt;
- if (x == Qundef) {
- rb_bug("undef leaked to the Ruby space");
+ if (RB_UNLIKELY(x == Qundef)) {
+ rb_bug(UNDEF_LEAKED);
}
xt = TYPE(x);
- if (xt != t || (xt == T_DATA && RTYPEDDATA_P(x))) {
- const char *tname = rb_builtin_type_name(t);
- if (tname) {
- rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)",
- builtin_class_name(x), tname);
- }
- if (xt > T_MASK && xt <= 0x3f) {
- rb_fatal("unknown type 0x%x (0x%x given, probably comes from extension library for ruby 1.8)", t, xt);
- }
- rb_bug("unknown type 0x%x (0x%x given)", t, xt);
+ if (xt != t || (xt == T_DATA && rbimpl_rtypeddata_p(x))) {
+ /*
+ * Typed data is not simple `T_DATA`, but in a sense an
+ * extension of `struct RVALUE`, which are incompatible with
+ * each other except when inherited.
+ *
+ * So it is not enough to just check `T_DATA`, it must be
+ * identified by its `type` using `Check_TypedStruct` instead.
+ */
+ unexpected_type(x, xt, t);
+ }
+}
+
+void
+rb_unexpected_type(VALUE x, int t)
+{
+ if (RB_UNLIKELY(x == Qundef)) {
+ rb_bug(UNDEF_LEAKED);
}
+
+ unexpected_type(x, TYPE(x), t);
}
int
@@ -501,25 +1055,36 @@ rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
return 1;
}
+#undef rb_typeddata_is_instance_of
+int
+rb_typeddata_is_instance_of(VALUE obj, const rb_data_type_t *data_type)
+{
+ return rb_typeddata_is_instance_of_inline(obj, data_type);
+}
+
void *
rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type)
{
- const char *etype;
- static const char mesg[] = "wrong argument type %s (expected %s)";
+ VALUE actual;
if (!RB_TYPE_P(obj, T_DATA)) {
- etype = builtin_class_name(obj);
- rb_raise(rb_eTypeError, mesg, etype, data_type->wrap_struct_name);
+ actual = displaying_class_of(obj);
}
- if (!RTYPEDDATA_P(obj)) {
- etype = rb_obj_classname(obj);
- rb_raise(rb_eTypeError, mesg, etype, data_type->wrap_struct_name);
+ else if (!RTYPEDDATA_P(obj)) {
+ actual = displaying_class_of(obj);
}
else if (!rb_typeddata_inherited_p(RTYPEDDATA_TYPE(obj), data_type)) {
- etype = RTYPEDDATA_TYPE(obj)->wrap_struct_name;
- rb_raise(rb_eTypeError, mesg, etype, data_type->wrap_struct_name);
+ const char *name = RTYPEDDATA_TYPE(obj)->wrap_struct_name;
+ actual = rb_str_new_cstr(name); /* or rb_fstring_cstr? not sure... */
}
- return DATA_PTR(obj);
+ else {
+ return DATA_PTR(obj);
+ }
+
+ const char *expected = data_type->wrap_struct_name;
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected %s)",
+ actual, expected);
+ UNREACHABLE_RETURN(NULL);
}
/* exception classes */
@@ -530,6 +1095,7 @@ VALUE rb_eSignal;
VALUE rb_eFatal;
VALUE rb_eStandardError;
VALUE rb_eRuntimeError;
+VALUE rb_eFrozenError;
VALUE rb_eTypeError;
VALUE rb_eArgError;
VALUE rb_eIndexError;
@@ -543,6 +1109,8 @@ VALUE rb_eSecurityError;
VALUE rb_eNotImpError;
VALUE rb_eNoMemError;
VALUE rb_cNameErrorMesg;
+VALUE rb_eNoMatchingPatternError;
+VALUE rb_eNoMatchingPatternKeyError;
VALUE rb_eScriptError;
VALUE rb_eSyntaxError;
@@ -552,30 +1120,52 @@ VALUE rb_eSystemCallError;
VALUE rb_mErrno;
static VALUE rb_eNOERROR;
-#undef rb_exc_new2
+ID ruby_static_id_cause;
+#define id_cause ruby_static_id_cause
+static ID id_message, id_backtrace;
+static ID id_key, id_matchee, id_args, id_Errno, id_errno, id_i_path;
+static ID id_receiver, id_recv, id_iseq, id_local_variables;
+static ID id_private_call_p, id_top, id_bottom;
+#define id_bt idBt
+#define id_bt_locations idBt_locations
+#define id_mesg idMesg
+#define id_name idName
+
+#undef rb_exc_new_cstr
VALUE
rb_exc_new(VALUE etype, const char *ptr, long len)
{
- return rb_funcall(etype, rb_intern("new"), 1, rb_str_new(ptr, len));
+ VALUE mesg = rb_str_new(ptr, len);
+ return rb_class_new_instance(1, &mesg, etype);
}
VALUE
-rb_exc_new2(VALUE etype, const char *s)
+rb_exc_new_cstr(VALUE etype, const char *s)
{
return rb_exc_new(etype, s, strlen(s));
}
VALUE
-rb_exc_new3(VALUE etype, VALUE str)
+rb_exc_new_str(VALUE etype, VALUE str)
{
StringValue(str);
- return rb_funcall(etype, rb_intern("new"), 1, str);
+ return rb_class_new_instance(1, &str, etype);
+}
+
+static VALUE
+exc_init(VALUE exc, VALUE mesg)
+{
+ rb_ivar_set(exc, id_mesg, mesg);
+ rb_ivar_set(exc, id_bt, Qnil);
+
+ return exc;
}
/*
* call-seq:
- * Exception.new(msg = nil) -> exception
+ * Exception.new(msg = nil) -> exception
+ * Exception.exception(msg = nil) -> exception
*
* Construct a new Exception object, optionally passing in
* a message.
@@ -586,18 +1176,15 @@ exc_initialize(int argc, VALUE *argv, VALUE exc)
{
VALUE arg;
- rb_scan_args(argc, argv, "01", &arg);
- rb_iv_set(exc, "mesg", arg);
- rb_iv_set(exc, "bt", Qnil);
-
- return exc;
+ arg = (!rb_check_arity(argc, 0, 1) ? Qnil : argv[0]);
+ return exc_init(exc, arg);
}
/*
* Document-method: exception
*
* call-seq:
- * exc.exception(string) -> an_exception or exc
+ * exc.exception([string]) -> an_exception or exc
*
* With no argument, or if the argument is the same as the receiver,
* return the receiver. Otherwise, create a new
@@ -611,11 +1198,11 @@ exc_exception(int argc, VALUE *argv, VALUE self)
{
VALUE exc;
+ argc = rb_check_arity(argc, 0, 1);
if (argc == 0) return self;
if (argc == 1 && self == argv[0]) return self;
exc = rb_obj_clone(self);
- exc_initialize(argc, argv, exc);
-
+ rb_ivar_set(exc, id_mesg, argv[0]);
return exc;
}
@@ -630,12 +1217,97 @@ exc_exception(int argc, VALUE *argv, VALUE self)
static VALUE
exc_to_s(VALUE exc)
{
- VALUE mesg = rb_attr_get(exc, rb_intern("mesg"));
- VALUE r = Qnil;
+ VALUE mesg = rb_attr_get(exc, idMesg);
if (NIL_P(mesg)) return rb_class_name(CLASS_OF(exc));
- r = rb_String(mesg);
- return r;
+ return rb_String(mesg);
+}
+
+/* FIXME: Include eval_error.c */
+void rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE highlight, VALUE reverse);
+
+VALUE
+rb_get_message(VALUE exc)
+{
+ VALUE e = rb_check_funcall(exc, id_message, 0, 0);
+ if (e == Qundef) return Qnil;
+ if (!RB_TYPE_P(e, T_STRING)) e = rb_check_string_type(e);
+ return e;
+}
+
+/*
+ * call-seq:
+ * Exception.to_tty? -> true or false
+ *
+ * Returns +true+ if exception messages will be sent to a tty.
+ */
+static VALUE
+exc_s_to_tty_p(VALUE self)
+{
+ return RBOOL(rb_stderr_tty_p());
+}
+
+/*
+ * call-seq:
+ * exception.full_message(highlight: bool, order: [:top or :bottom]) -> string
+ *
+ * Returns formatted string of _exception_.
+ * The returned string is formatted using the same format that Ruby uses
+ * when printing an uncaught exceptions to stderr.
+ *
+ * If _highlight_ is +true+ the default error handler will send the
+ * messages to a tty.
+ *
+ * _order_ must be either of +:top+ or +:bottom+, and places the error
+ * message and the innermost backtrace come at the top or the bottom.
+ *
+ * The default values of these options depend on <code>$stderr</code>
+ * and its +tty?+ at the timing of a call.
+ */
+
+static VALUE
+exc_full_message(int argc, VALUE *argv, VALUE exc)
+{
+ VALUE opt, str, emesg, errat;
+ enum {kw_highlight, kw_order, kw_max_};
+ static ID kw[kw_max_];
+ VALUE args[kw_max_] = {Qnil, Qnil};
+
+ rb_scan_args(argc, argv, "0:", &opt);
+ if (!NIL_P(opt)) {
+ if (!kw[0]) {
+#define INIT_KW(n) kw[kw_##n] = rb_intern_const(#n)
+ INIT_KW(highlight);
+ INIT_KW(order);
+#undef INIT_KW
+ }
+ rb_get_kwargs(opt, kw, 0, kw_max_, args);
+ switch (args[kw_highlight]) {
+ default:
+ rb_bool_expected(args[kw_highlight], "highlight");
+ UNREACHABLE;
+ case Qundef: args[kw_highlight] = Qnil; break;
+ case Qtrue: case Qfalse: case Qnil: break;
+ }
+ if (args[kw_order] == Qundef) {
+ args[kw_order] = Qnil;
+ }
+ else {
+ ID id = rb_check_id(&args[kw_order]);
+ if (id == id_bottom) args[kw_order] = Qtrue;
+ else if (id == id_top) args[kw_order] = Qfalse;
+ else {
+ rb_raise(rb_eArgError, "expected :top or :bottom as "
+ "order: %+"PRIsVALUE, args[kw_order]);
+ }
+ }
+ }
+ str = rb_str_new2("");
+ errat = rb_get_backtrace(exc);
+ emesg = rb_get_message(exc);
+
+ rb_error_write(exc, emesg, errat, str, args[kw_highlight], args[kw_order]);
+ return str;
}
/*
@@ -643,22 +1315,20 @@ exc_to_s(VALUE exc)
* exception.message -> string
*
* Returns the result of invoking <code>exception.to_s</code>.
- * Normally this returns the exception's message or name. By
- * supplying a to_str method, exceptions are agreeing to
- * be used where Strings are expected.
+ * Normally this returns the exception's message or name.
*/
static VALUE
exc_message(VALUE exc)
{
- return rb_funcall(exc, rb_intern("to_s"), 0, 0);
+ return rb_funcallv(exc, idTo_s, 0, 0);
}
/*
* call-seq:
* exception.inspect -> string
*
- * Return this exception's class name and message
+ * Return this exception's class name and message.
*/
static VALUE
@@ -669,7 +1339,7 @@ exc_inspect(VALUE exc)
klass = CLASS_OF(exc);
exc = rb_obj_as_string(exc);
if (RSTRING_LEN(exc) == 0) {
- return rb_str_dup(rb_class_name(klass));
+ return rb_class_name(klass);
}
str = rb_str_buf_new2("#<");
@@ -684,7 +1354,7 @@ exc_inspect(VALUE exc)
/*
* call-seq:
- * exception.backtrace -> array
+ * exception.backtrace -> array or nil
*
* Returns any backtrace associated with the exception. The backtrace
* is an array of strings, each containing either ``filename:lineNo: in
@@ -709,26 +1379,75 @@ exc_inspect(VALUE exc)
* prog.rb:2:in `a'
* prog.rb:6:in `b'
* prog.rb:10
+ *
+ * In the case no backtrace has been set, +nil+ is returned
+ *
+ * ex = StandardError.new
+ * ex.backtrace
+ * #=> nil
*/
static VALUE
exc_backtrace(VALUE exc)
{
- ID bt;
VALUE obj;
- CONST_ID(bt, "bt");
- obj = rb_attr_get(exc, bt);
+ obj = rb_attr_get(exc, id_bt);
if (rb_backtrace_p(obj)) {
obj = rb_backtrace_to_str_ary(obj);
- /* rb_iv_set(exc, "bt", obj); */
+ /* rb_ivar_set(exc, id_bt, obj); */
}
return obj;
}
+static VALUE rb_check_backtrace(VALUE);
+
VALUE
+rb_get_backtrace(VALUE exc)
+{
+ ID mid = id_backtrace;
+ VALUE info;
+ if (rb_method_basic_definition_p(CLASS_OF(exc), id_backtrace)) {
+ VALUE klass = rb_eException;
+ rb_execution_context_t *ec = GET_EC();
+ if (NIL_P(exc))
+ return Qnil;
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, exc, mid, mid, klass, Qundef);
+ info = exc_backtrace(exc);
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, exc, mid, mid, klass, info);
+ }
+ else {
+ info = rb_funcallv(exc, mid, 0, 0);
+ }
+ if (NIL_P(info)) return Qnil;
+ return rb_check_backtrace(info);
+}
+
+/*
+ * call-seq:
+ * exception.backtrace_locations -> array or nil
+ *
+ * Returns any backtrace associated with the exception. This method is
+ * similar to Exception#backtrace, but the backtrace is an array of
+ * Thread::Backtrace::Location.
+ *
+ * This method is not affected by Exception#set_backtrace().
+ */
+static VALUE
+exc_backtrace_locations(VALUE exc)
+{
+ VALUE obj;
+
+ obj = rb_attr_get(exc, id_bt_locations);
+ if (!NIL_P(obj)) {
+ obj = rb_backtrace_to_location_ary(obj);
+ }
+ return obj;
+}
+
+static VALUE
rb_check_backtrace(VALUE bt)
{
long i;
@@ -741,7 +1460,8 @@ rb_check_backtrace(VALUE bt)
rb_raise(rb_eTypeError, err);
}
for (i=0;i<RARRAY_LEN(bt);i++) {
- if (!RB_TYPE_P(RARRAY_PTR(bt)[i], T_STRING)) {
+ VALUE e = RARRAY_AREF(bt, i);
+ if (!RB_TYPE_P(e, T_STRING)) {
rb_raise(rb_eTypeError, err);
}
}
@@ -762,28 +1482,41 @@ rb_check_backtrace(VALUE bt)
static VALUE
exc_set_backtrace(VALUE exc, VALUE bt)
{
- return rb_iv_set(exc, "bt", rb_check_backtrace(bt));
+ return rb_ivar_set(exc, id_bt, rb_check_backtrace(bt));
}
-VALUE
+MJIT_FUNC_EXPORTED VALUE
rb_exc_set_backtrace(VALUE exc, VALUE bt)
{
return exc_set_backtrace(exc, bt);
}
+/*
+ * call-seq:
+ * exception.cause -> an_exception or nil
+ *
+ * Returns the previous exception ($!) at the time this exception was raised.
+ * This is useful for wrapping exceptions and retaining the original exception
+ * information.
+ */
+
+static VALUE
+exc_cause(VALUE exc)
+{
+ return rb_attr_get(exc, id_cause);
+}
+
static VALUE
try_convert_to_exception(VALUE obj)
{
- ID id_exception;
- CONST_ID(id_exception, "exception");
- return rb_check_funcall(obj, id_exception, 0, 0);
+ return rb_check_funcall(obj, idException, 0, 0);
}
/*
* call-seq:
* exc == obj -> true or false
*
- * Equality---If <i>obj</i> is not an <code>Exception</code>, returns
+ * Equality---If <i>obj</i> is not an Exception, returns
* <code>false</code>. Otherwise, returns <code>true</code> if <i>exc</i> and
* <i>obj</i> share same class, messages, and backtrace.
*/
@@ -792,19 +1525,14 @@ static VALUE
exc_equal(VALUE exc, VALUE obj)
{
VALUE mesg, backtrace;
- ID id_mesg;
if (exc == obj) return Qtrue;
- CONST_ID(id_mesg, "mesg");
if (rb_obj_class(exc) != rb_obj_class(obj)) {
- int status = 0;
- ID id_message, id_backtrace;
- CONST_ID(id_message, "message");
- CONST_ID(id_backtrace, "backtrace");
+ int state;
- obj = rb_protect(try_convert_to_exception, obj, &status);
- if (status || obj == Qundef) {
+ obj = rb_protect(try_convert_to_exception, obj, &state);
+ if (state || obj == Qundef) {
rb_set_errinfo(Qnil);
return Qfalse;
}
@@ -821,9 +1549,7 @@ exc_equal(VALUE exc, VALUE obj)
if (!rb_equal(rb_attr_get(exc, id_mesg), mesg))
return Qfalse;
- if (!rb_equal(exc_backtrace(exc), backtrace))
- return Qfalse;
- return Qtrue;
+ return rb_equal(exc_backtrace(exc), backtrace);
}
/*
@@ -876,14 +1602,14 @@ exit_initialize(int argc, VALUE *argv, VALUE exc)
status = INT2FIX(EXIT_SUCCESS);
}
rb_call_super(argc, argv);
- rb_iv_set(exc, "status", status);
+ rb_ivar_set(exc, id_status, status);
return exc;
}
/*
* call-seq:
- * system_exit.status -> fixnum
+ * system_exit.status -> integer
*
* Return the status value associated with this system exit.
*/
@@ -891,7 +1617,7 @@ exit_initialize(int argc, VALUE *argv, VALUE exc)
static VALUE
exit_status(VALUE exc)
{
- return rb_attr_get(exc, rb_intern("status"));
+ return rb_attr_get(exc, id_status);
}
@@ -905,18 +1631,58 @@ exit_status(VALUE exc)
static VALUE
exit_success_p(VALUE exc)
{
- VALUE status_val = rb_attr_get(exc, rb_intern("status"));
+ VALUE status_val = rb_attr_get(exc, id_status);
int status;
if (NIL_P(status_val))
return Qtrue;
status = NUM2INT(status_val);
- if (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS)
- return Qtrue;
+ return RBOOL(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
+}
- return Qfalse;
+static VALUE
+err_init_recv(VALUE exc, VALUE recv)
+{
+ if (recv != Qundef) rb_ivar_set(exc, id_recv, recv);
+ return exc;
+}
+
+/*
+ * call-seq:
+ * FrozenError.new(msg=nil, receiver: nil) -> frozen_error
+ *
+ * Construct a new FrozenError exception. If given the <i>receiver</i>
+ * parameter may subsequently be examined using the FrozenError#receiver
+ * method.
+ *
+ * a = [].freeze
+ * raise FrozenError.new("can't modify frozen array", receiver: a)
+ */
+
+static VALUE
+frozen_err_initialize(int argc, VALUE *argv, VALUE self)
+{
+ ID keywords[1];
+ VALUE values[numberof(keywords)], options;
+
+ argc = rb_scan_args(argc, argv, "*:", NULL, &options);
+ keywords[0] = id_receiver;
+ rb_get_kwargs(options, keywords, 0, numberof(values), values);
+ rb_call_super(argc, argv);
+ err_init_recv(self, values[0]);
+ return self;
}
+/*
+ * Document-method: FrozenError#receiver
+ * call-seq:
+ * frozen_error.receiver -> object
+ *
+ * Return the receiver associated with this FrozenError exception.
+ */
+
+#define frozen_err_receiver name_err_receiver
+
void
rb_name_error(ID id, const char *fmt, ...)
{
@@ -947,26 +1713,62 @@ rb_name_error_str(VALUE str, const char *fmt, ...)
rb_exc_raise(exc);
}
+static VALUE
+name_err_init_attr(VALUE exc, VALUE recv, VALUE method)
+{
+ const rb_execution_context_t *ec = GET_EC();
+ rb_control_frame_t *cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(ec->cfp);
+ cfp = rb_vm_get_ruby_level_next_cfp(ec, cfp);
+ rb_ivar_set(exc, id_name, method);
+ err_init_recv(exc, recv);
+ if (cfp) rb_ivar_set(exc, id_iseq, rb_iseqw_new(cfp->iseq));
+ return exc;
+}
+
/*
* call-seq:
- * NameError.new(msg [, name]) -> name_error
+ * NameError.new(msg=nil, name=nil, receiver: nil) -> name_error
*
* Construct a new NameError exception. If given the <i>name</i>
- * parameter may subsequently be examined using the <code>NameError.name</code>
- * method.
+ * parameter may subsequently be examined using the NameError#name
+ * method. <i>receiver</i> parameter allows to pass object in
+ * context of which the error happened. Example:
+ *
+ * [1, 2, 3].method(:rject) # NameError with name "rject" and receiver: Array
+ * [1, 2, 3].singleton_method(:rject) # NameError with name "rject" and receiver: [1, 2, 3]
*/
static VALUE
name_err_initialize(int argc, VALUE *argv, VALUE self)
{
- VALUE name;
+ ID keywords[1];
+ VALUE values[numberof(keywords)], name, options;
+ argc = rb_scan_args(argc, argv, "*:", NULL, &options);
+ keywords[0] = id_receiver;
+ rb_get_kwargs(options, keywords, 0, numberof(values), values);
name = (argc > 1) ? argv[--argc] : Qnil;
rb_call_super(argc, argv);
- rb_iv_set(self, "name", name);
+ name_err_init_attr(self, values[0], name);
return self;
}
+static VALUE rb_name_err_mesg_new(VALUE mesg, VALUE recv, VALUE method);
+
+static VALUE
+name_err_init(VALUE exc, VALUE mesg, VALUE recv, VALUE method)
+{
+ exc_init(exc, rb_name_err_mesg_new(mesg, recv, method));
+ return name_err_init_attr(exc, recv, method);
+}
+
+VALUE
+rb_name_err_new(VALUE mesg, VALUE recv, VALUE method)
+{
+ VALUE exc = rb_obj_alloc(rb_eNameError);
+ return name_err_init(exc, mesg, recv, method);
+}
+
/*
* call-seq:
* name_error.name -> string or nil
@@ -977,48 +1779,85 @@ name_err_initialize(int argc, VALUE *argv, VALUE self)
static VALUE
name_err_name(VALUE self)
{
- return rb_attr_get(self, rb_intern("name"));
+ return rb_attr_get(self, id_name);
}
/*
- * call-seq:
- * name_error.to_s -> string
+ * call-seq:
+ * name_error.local_variables -> array
+ *
+ * Return a list of the local variable names defined where this
+ * NameError exception was raised.
*
- * Produce a nicely-formatted string representing the +NameError+.
+ * Internal use only.
*/
static VALUE
-name_err_to_s(VALUE exc)
+name_err_local_variables(VALUE self)
{
- VALUE mesg = rb_attr_get(exc, rb_intern("mesg"));
- VALUE str = mesg;
+ VALUE vars = rb_attr_get(self, id_local_variables);
- if (NIL_P(mesg)) return rb_class_name(CLASS_OF(exc));
- StringValue(str);
- return str;
+ if (NIL_P(vars)) {
+ VALUE iseqw = rb_attr_get(self, id_iseq);
+ if (!NIL_P(iseqw)) vars = rb_iseqw_local_variables(iseqw);
+ if (NIL_P(vars)) vars = rb_ary_new();
+ rb_ivar_set(self, id_local_variables, vars);
+ }
+ return vars;
+}
+
+static VALUE
+nometh_err_init_attr(VALUE exc, VALUE args, int priv)
+{
+ rb_ivar_set(exc, id_args, args);
+ rb_ivar_set(exc, id_private_call_p, RBOOL(priv));
+ return exc;
}
/*
* call-seq:
- * NoMethodError.new(msg, name [, args]) -> no_method_error
+ * NoMethodError.new(msg=nil, name=nil, args=nil, private=false, receiver: nil) -> no_method_error
*
* Construct a NoMethodError exception for a method of the given name
* called with the given arguments. The name may be accessed using
* the <code>#name</code> method on the resulting object, and the
* arguments using the <code>#args</code> method.
+ *
+ * If <i>private</i> argument were passed, it designates method was
+ * attempted to call in private context, and can be accessed with
+ * <code>#private_call?</code> method.
+ *
+ * <i>receiver</i> argument stores an object whose method was called.
*/
static VALUE
nometh_err_initialize(int argc, VALUE *argv, VALUE self)
{
- VALUE args = (argc > 2) ? argv[--argc] : Qnil;
- name_err_initialize(argc, argv, self);
- rb_iv_set(self, "args", args);
- return self;
+ int priv;
+ VALUE args, options;
+ argc = rb_scan_args(argc, argv, "*:", NULL, &options);
+ priv = (argc > 3) && (--argc, RTEST(argv[argc]));
+ args = (argc > 2) ? argv[--argc] : Qnil;
+ if (!NIL_P(options)) argv[argc++] = options;
+ rb_call_super_kw(argc, argv, RB_PASS_CALLED_KEYWORDS);
+ return nometh_err_init_attr(self, args, priv);
+}
+
+VALUE
+rb_nomethod_err_new(VALUE mesg, VALUE recv, VALUE method, VALUE args, int priv)
+{
+ VALUE exc = rb_obj_alloc(rb_eNoMethodError);
+ name_err_init(exc, mesg, recv, method);
+ return nometh_err_init_attr(exc, args, priv);
}
/* :nodoc: */
-#define NAME_ERR_MESG_COUNT 3
+enum {
+ NAME_ERR_MESG__MESG,
+ NAME_ERR_MESG__RECV,
+ NAME_ERR_MESG__NAME,
+ NAME_ERR_MESG_COUNT
+};
static void
name_err_mesg_mark(void *p)
@@ -1032,7 +1871,7 @@ name_err_mesg_mark(void *p)
static size_t
name_err_mesg_memsize(const void *p)
{
- return p ? (NAME_ERR_MESG_COUNT * sizeof(VALUE)) : 0;
+ return NAME_ERR_MESG_COUNT * sizeof(VALUE);
}
static const rb_data_type_t name_err_mesg_data_type = {
@@ -1042,27 +1881,54 @@ static const rb_data_type_t name_err_mesg_data_type = {
name_err_mesg_free,
name_err_mesg_memsize,
},
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
/* :nodoc: */
-VALUE
-rb_name_err_mesg_new(VALUE obj, VALUE mesg, VALUE recv, VALUE method)
+static VALUE
+rb_name_err_mesg_init(VALUE klass, VALUE mesg, VALUE recv, VALUE method)
{
+ VALUE result = TypedData_Wrap_Struct(klass, &name_err_mesg_data_type, 0);
VALUE *ptr = ALLOC_N(VALUE, NAME_ERR_MESG_COUNT);
- VALUE result;
-
- ptr[0] = mesg;
- ptr[1] = recv;
- ptr[2] = method;
- result = TypedData_Wrap_Struct(rb_cNameErrorMesg, &name_err_mesg_data_type, ptr);
- RB_GC_GUARD(mesg);
- RB_GC_GUARD(recv);
- RB_GC_GUARD(method);
+
+ ptr[NAME_ERR_MESG__MESG] = mesg;
+ ptr[NAME_ERR_MESG__RECV] = recv;
+ ptr[NAME_ERR_MESG__NAME] = method;
+ RTYPEDDATA_DATA(result) = ptr;
return result;
}
/* :nodoc: */
static VALUE
+rb_name_err_mesg_new(VALUE mesg, VALUE recv, VALUE method)
+{
+ return rb_name_err_mesg_init(rb_cNameErrorMesg, mesg, recv, method);
+}
+
+/* :nodoc: */
+static VALUE
+name_err_mesg_alloc(VALUE klass)
+{
+ return rb_name_err_mesg_init(klass, Qnil, Qnil, Qnil);
+}
+
+/* :nodoc: */
+static VALUE
+name_err_mesg_init_copy(VALUE obj1, VALUE obj2)
+{
+ VALUE *ptr1, *ptr2;
+
+ if (obj1 == obj2) return obj1;
+ rb_obj_init_copy(obj1, obj2);
+
+ TypedData_Get_Struct(obj1, VALUE, &name_err_mesg_data_type, ptr1);
+ TypedData_Get_Struct(obj2, VALUE, &name_err_mesg_data_type, ptr2);
+ MEMCPY(ptr1, ptr2, VALUE, NAME_ERR_MESG_COUNT);
+ return obj1;
+}
+
+/* :nodoc: */
+static VALUE
name_err_mesg_equal(VALUE obj1, VALUE obj2)
{
VALUE *ptr1, *ptr2;
@@ -1083,54 +1949,81 @@ name_err_mesg_equal(VALUE obj1, VALUE obj2)
/* :nodoc: */
static VALUE
+name_err_mesg_receiver_name(VALUE obj)
+{
+ if (RB_SPECIAL_CONST_P(obj)) return Qundef;
+ if (RB_BUILTIN_TYPE(obj) == T_MODULE || RB_BUILTIN_TYPE(obj) == T_CLASS) {
+ return rb_check_funcall(obj, rb_intern("name"), 0, 0);
+ }
+ return Qundef;
+}
+
+/* :nodoc: */
+static VALUE
name_err_mesg_to_str(VALUE obj)
{
VALUE *ptr, mesg;
TypedData_Get_Struct(obj, VALUE, &name_err_mesg_data_type, ptr);
- mesg = ptr[0];
+ mesg = ptr[NAME_ERR_MESG__MESG];
if (NIL_P(mesg)) return Qnil;
else {
- const char *desc = 0;
- VALUE d = 0, args[NAME_ERR_MESG_COUNT];
- int state = 0;
+ struct RString s_str, d_str;
+ VALUE c, s, d = 0, args[4];
+ int state = 0, singleton = 0;
+ rb_encoding *usascii = rb_usascii_encoding();
- obj = ptr[1];
+#define FAKE_CSTR(v, str) rb_setup_fake_str((v), (str), rb_strlen_lit(str), usascii)
+ obj = ptr[NAME_ERR_MESG__RECV];
switch (obj) {
case Qnil:
- desc = "nil";
+ d = FAKE_CSTR(&d_str, "nil");
break;
case Qtrue:
- desc = "true";
+ d = FAKE_CSTR(&d_str, "true");
break;
case Qfalse:
- desc = "false";
+ d = FAKE_CSTR(&d_str, "false");
break;
default:
- d = rb_protect(rb_inspect, obj, &state);
- if (state)
+ d = rb_protect(name_err_mesg_receiver_name, obj, &state);
+ if (state || d == Qundef || NIL_P(d))
+ d = rb_protect(rb_inspect, obj, &state);
+ if (state) {
rb_set_errinfo(Qnil);
- if (NIL_P(d) || RSTRING_LEN(d) > 65) {
+ }
+ d = rb_check_string_type(d);
+ if (NIL_P(d)) {
d = rb_any_to_s(obj);
}
- desc = RSTRING_PTR(d);
+ singleton = (RSTRING_LEN(d) > 0 && RSTRING_PTR(d)[0] == '#');
break;
}
- if (desc && desc[0] != '#') {
- d = d ? rb_str_dup(d) : rb_str_new2(desc);
- rb_str_cat2(d, ":");
- rb_str_cat2(d, rb_obj_classname(obj));
+ if (!singleton) {
+ s = FAKE_CSTR(&s_str, ":");
+ c = rb_class_name(CLASS_OF(obj));
}
- args[0] = mesg;
- args[1] = ptr[2];
- args[2] = d;
- mesg = rb_f_sprintf(NAME_ERR_MESG_COUNT, args);
+ else {
+ c = s = FAKE_CSTR(&s_str, "");
+ }
+ args[0] = rb_obj_as_string(ptr[NAME_ERR_MESG__NAME]);
+ args[1] = d;
+ args[2] = s;
+ args[3] = c;
+ mesg = rb_str_format(4, args, mesg);
}
return mesg;
}
/* :nodoc: */
static VALUE
+name_err_mesg_dump(VALUE obj, VALUE limit)
+{
+ return name_err_mesg_to_str(obj);
+}
+
+/* :nodoc: */
+static VALUE
name_err_mesg_load(VALUE klass, VALUE str)
{
return str;
@@ -1138,6 +2031,29 @@ name_err_mesg_load(VALUE klass, VALUE str)
/*
* call-seq:
+ * name_error.receiver -> object
+ *
+ * Return the receiver associated with this NameError exception.
+ */
+
+static VALUE
+name_err_receiver(VALUE self)
+{
+ VALUE *ptr, recv, mesg;
+
+ recv = rb_ivar_lookup(self, id_recv, Qundef);
+ if (recv != Qundef) return recv;
+
+ mesg = rb_attr_get(self, id_mesg);
+ if (!rb_typeddata_is_kind_of(mesg, &name_err_mesg_data_type)) {
+ rb_raise(rb_eArgError, "no receiver is available");
+ }
+ ptr = DATA_PTR(mesg);
+ return ptr[NAME_ERR_MESG__RECV];
+}
+
+/*
+ * call-seq:
* no_method_error.args -> obj
*
* Return the arguments passed in as the third parameter to
@@ -1147,7 +2063,20 @@ name_err_mesg_load(VALUE klass, VALUE str)
static VALUE
nometh_err_args(VALUE self)
{
- return rb_attr_get(self, rb_intern("args"));
+ return rb_attr_get(self, id_args);
+}
+
+/*
+ * call-seq:
+ * no_method_error.private_call? -> true or false
+ *
+ * Return true if the caused method was called as private.
+ */
+
+static VALUE
+nometh_err_private_call_p(VALUE self)
+{
+ return rb_attr_get(self, id_private_call_p);
}
void
@@ -1159,21 +2088,183 @@ rb_invalid_str(const char *str, const char *type)
}
/*
+ * call-seq:
+ * key_error.receiver -> object
+ *
+ * Return the receiver associated with this KeyError exception.
+ */
+
+static VALUE
+key_err_receiver(VALUE self)
+{
+ VALUE recv;
+
+ recv = rb_ivar_lookup(self, id_receiver, Qundef);
+ if (recv != Qundef) return recv;
+ rb_raise(rb_eArgError, "no receiver is available");
+}
+
+/*
+ * call-seq:
+ * key_error.key -> object
+ *
+ * Return the key caused this KeyError exception.
+ */
+
+static VALUE
+key_err_key(VALUE self)
+{
+ VALUE key;
+
+ key = rb_ivar_lookup(self, id_key, Qundef);
+ if (key != Qundef) return key;
+ rb_raise(rb_eArgError, "no key is available");
+}
+
+VALUE
+rb_key_err_new(VALUE mesg, VALUE recv, VALUE key)
+{
+ VALUE exc = rb_obj_alloc(rb_eKeyError);
+ rb_ivar_set(exc, id_mesg, mesg);
+ rb_ivar_set(exc, id_bt, Qnil);
+ rb_ivar_set(exc, id_key, key);
+ rb_ivar_set(exc, id_receiver, recv);
+ return exc;
+}
+
+/*
+ * call-seq:
+ * KeyError.new(message=nil, receiver: nil, key: nil) -> key_error
+ *
+ * Construct a new +KeyError+ exception with the given message,
+ * receiver and key.
+ */
+
+static VALUE
+key_err_initialize(int argc, VALUE *argv, VALUE self)
+{
+ VALUE options;
+
+ rb_call_super(rb_scan_args(argc, argv, "01:", NULL, &options), argv);
+
+ if (!NIL_P(options)) {
+ ID keywords[2];
+ VALUE values[numberof(keywords)];
+ int i;
+ keywords[0] = id_receiver;
+ keywords[1] = id_key;
+ rb_get_kwargs(options, keywords, 0, numberof(values), values);
+ for (i = 0; i < numberof(values); ++i) {
+ if (values[i] != Qundef) {
+ rb_ivar_set(self, keywords[i], values[i]);
+ }
+ }
+ }
+
+ return self;
+}
+
+/*
+ * call-seq:
+ * no_matching_pattern_key_error.matchee -> object
+ *
+ * Return the matchee associated with this NoMatchingPatternKeyError exception.
+ */
+
+static VALUE
+no_matching_pattern_key_err_matchee(VALUE self)
+{
+ VALUE matchee;
+
+ matchee = rb_ivar_lookup(self, id_matchee, Qundef);
+ if (matchee != Qundef) return matchee;
+ rb_raise(rb_eArgError, "no matchee is available");
+}
+
+/*
+ * call-seq:
+ * no_matching_pattern_key_error.key -> object
+ *
+ * Return the key caused this NoMatchingPatternKeyError exception.
+ */
+
+static VALUE
+no_matching_pattern_key_err_key(VALUE self)
+{
+ VALUE key;
+
+ key = rb_ivar_lookup(self, id_key, Qundef);
+ if (key != Qundef) return key;
+ rb_raise(rb_eArgError, "no key is available");
+}
+
+/*
+ * call-seq:
+ * NoMatchingPatternKeyError.new(message=nil, matchee: nil, key: nil) -> no_matching_pattern_key_error
+ *
+ * Construct a new +NoMatchingPatternKeyError+ exception with the given message,
+ * matchee and key.
+ */
+
+static VALUE
+no_matching_pattern_key_err_initialize(int argc, VALUE *argv, VALUE self)
+{
+ VALUE options;
+
+ rb_call_super(rb_scan_args(argc, argv, "01:", NULL, &options), argv);
+
+ if (!NIL_P(options)) {
+ ID keywords[2];
+ VALUE values[numberof(keywords)];
+ int i;
+ keywords[0] = id_matchee;
+ keywords[1] = id_key;
+ rb_get_kwargs(options, keywords, 0, numberof(values), values);
+ for (i = 0; i < numberof(values); ++i) {
+ if (values[i] != Qundef) {
+ rb_ivar_set(self, keywords[i], values[i]);
+ }
+ }
+ }
+
+ return self;
+}
+
+
+/*
+ * call-seq:
+ * SyntaxError.new([msg]) -> syntax_error
+ *
+ * Construct a SyntaxError exception.
+ */
+
+static VALUE
+syntax_error_initialize(int argc, VALUE *argv, VALUE self)
+{
+ VALUE mesg;
+ if (argc == 0) {
+ mesg = rb_fstring_lit("compile error");
+ argc = 1;
+ argv = &mesg;
+ }
+ return rb_call_super(argc, argv);
+}
+
+/*
* Document-module: Errno
*
- * Ruby exception objects are subclasses of <code>Exception</code>.
- * However, operating systems typically report errors using plain
- * integers. Module <code>Errno</code> is created dynamically to map
- * these operating system errors to Ruby classes, with each error
- * number generating its own subclass of <code>SystemCallError</code>.
- * As the subclass is created in module <code>Errno</code>, its name
- * will start <code>Errno::</code>.
+ * Ruby exception objects are subclasses of Exception. However,
+ * operating systems typically report errors using plain
+ * integers. Module Errno is created dynamically to map these
+ * operating system errors to Ruby classes, with each error number
+ * generating its own subclass of SystemCallError. As the subclass
+ * is created in module Errno, its name will start
+ * <code>Errno::</code>.
*
- * The names of the <code>Errno::</code> classes depend on
- * the environment in which Ruby runs. On a typical Unix or Windows
- * platform, there are <code>Errno</code> classes such as
- * <code>Errno::EACCES</code>, <code>Errno::EAGAIN</code>,
- * <code>Errno::EINTR</code>, and so on.
+ * The names of the <code>Errno::</code> classes depend on the
+ * environment in which Ruby runs. On a typical Unix or Windows
+ * platform, there are Errno classes such as Errno::EACCES,
+ * Errno::EAGAIN, Errno::EINTR, and so on.
*
* The integer operating system error number corresponding to a
* particular error is available as the class constant
@@ -1184,7 +2275,7 @@ rb_invalid_str(const char *str, const char *type)
* Errno::EINTR::Errno #=> 4
*
* The full list of operating system errors on your particular platform
- * are available as the constants of <code>Errno</code>.
+ * are available as the constants of Errno.
*
* Errno.constants #=> :E2BIG, :EACCES, :EADDRINUSE, :EADDRNOTAVAIL, ...
*/
@@ -1198,6 +2289,24 @@ set_syserr(int n, const char *name)
if (!st_lookup(syserr_tbl, n, &error)) {
error = rb_define_class_under(rb_mErrno, name, rb_eSystemCallError);
+
+ /* capture nonblock errnos for WaitReadable/WaitWritable subclasses */
+ switch (n) {
+ case EAGAIN:
+ rb_eEAGAIN = error;
+
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ break;
+ case EWOULDBLOCK:
+#endif
+
+ rb_eEWOULDBLOCK = error;
+ break;
+ case EINPROGRESS:
+ rb_eEINPROGRESS = error;
+ break;
+ }
+
rb_define_const(error, "Errno", INT2NUM(n));
st_add_direct(syserr_tbl, n, error);
}
@@ -1225,11 +2334,10 @@ get_syserr(int n)
* call-seq:
* SystemCallError.new(msg, errno) -> system_call_error_subclass
*
- * If _errno_ corresponds to a known system error code, constructs
- * the appropriate <code>Errno</code> class for that error, otherwise
- * constructs a generic <code>SystemCallError</code> object. The
- * error number is subsequently available via the <code>errno</code>
- * method.
+ * If _errno_ corresponds to a known system error code, constructs the
+ * appropriate Errno class for that error, otherwise constructs a
+ * generic SystemCallError object. The error number is subsequently
+ * available via the #errno method.
*/
static VALUE
@@ -1239,12 +2347,12 @@ syserr_initialize(int argc, VALUE *argv, VALUE self)
char *strerror();
#endif
const char *err;
- VALUE mesg, error;
+ VALUE mesg, error, func, errmsg;
VALUE klass = rb_obj_class(self);
if (klass == rb_eSystemCallError) {
st_data_t data = (st_data_t)klass;
- rb_scan_args(argc, argv, "11", &mesg, &error);
+ rb_scan_args(argc, argv, "12", &mesg, &error, &func);
if (argc == 1 && FIXNUM_P(mesg)) {
error = mesg; mesg = Qnil;
}
@@ -1254,40 +2362,33 @@ syserr_initialize(int argc, VALUE *argv, VALUE self)
if (!RB_TYPE_P(self, T_OBJECT)) { /* insurance to avoid type crash */
rb_raise(rb_eTypeError, "invalid instance type");
}
- RBASIC(self)->klass = klass;
+ RBASIC_SET_CLASS(self, klass);
}
}
else {
- rb_scan_args(argc, argv, "01", &mesg);
- error = rb_const_get(klass, rb_intern("Errno"));
+ rb_scan_args(argc, argv, "02", &mesg, &func);
+ error = rb_const_get(klass, id_Errno);
}
if (!NIL_P(error)) err = strerror(NUM2INT(error));
else err = "unknown error";
+
+ errmsg = rb_enc_str_new_cstr(err, rb_locale_encoding());
if (!NIL_P(mesg)) {
- rb_encoding *le = rb_locale_encoding();
VALUE str = StringValue(mesg);
- rb_encoding *me = rb_enc_get(mesg);
- mesg = rb_sprintf("%s - %.*s", err,
- (int)RSTRING_LEN(str), RSTRING_PTR(str));
- if (le != me && rb_enc_asciicompat(me)) {
- le = me;
- }/* else assume err is non ASCII string. */
- OBJ_INFECT(mesg, str);
- rb_enc_associate(mesg, le);
- }
- else {
- mesg = rb_str_new2(err);
- rb_enc_associate(mesg, rb_locale_encoding());
+ if (!NIL_P(func)) rb_str_catf(errmsg, " @ %"PRIsVALUE, func);
+ rb_str_catf(errmsg, " - %"PRIsVALUE, str);
}
+ mesg = errmsg;
+
rb_call_super(1, &mesg);
- rb_iv_set(self, "errno", error);
+ rb_ivar_set(self, id_errno, error);
return self;
}
/*
* call-seq:
- * system_call_error.errno -> fixnum
+ * system_call_error.errno -> integer
*
* Return this SystemCallError's error number.
*/
@@ -1295,7 +2396,7 @@ syserr_initialize(int argc, VALUE *argv, VALUE self)
static VALUE
syserr_errno(VALUE self)
{
- return rb_attr_get(self, rb_intern("errno"));
+ return rb_attr_get(self, id_errno);
}
/*
@@ -1310,23 +2411,18 @@ static VALUE
syserr_eqq(VALUE self, VALUE exc)
{
VALUE num, e;
- ID en;
-
- CONST_ID(en, "errno");
if (!rb_obj_is_kind_of(exc, rb_eSystemCallError)) {
- if (!rb_respond_to(exc, en)) return Qfalse;
+ if (!rb_respond_to(exc, id_errno)) return Qfalse;
}
else if (self == rb_eSystemCallError) return Qtrue;
- num = rb_attr_get(exc, rb_intern("errno"));
+ num = rb_attr_get(exc, id_errno);
if (NIL_P(num)) {
- num = rb_funcall(exc, en, 0, 0);
+ num = rb_funcallv(exc, id_errno, 0, 0);
}
- e = rb_const_get(self, rb_intern("Errno"));
- if (FIXNUM_P(num) ? num == e : rb_equal(num, e))
- return Qtrue;
- return Qfalse;
+ e = rb_const_get(self, id_Errno);
+ return RBOOL(FIXNUM_P(num) ? num == e : rb_equal(num, e));
}
@@ -1365,6 +2461,7 @@ syserr_eqq(VALUE self, VALUE exc)
*
* begin
* Process.kill('HUP',Process.pid)
+ * sleep # wait for receiver to handle signal sent by Process.kill
* rescue SignalException => e
* puts "received Exception #{e}"
* end
@@ -1377,8 +2474,8 @@ syserr_eqq(VALUE self, VALUE exc)
/*
* Document-class: Interrupt
*
- * Raised with the interrupt signal is received, typically because the
- * user pressed on Control-C (on most posix platforms). As such, it is a
+ * Raised when the interrupt signal is received, typically because the
+ * user has pressed Control-C (on most posix platforms). As such, it is a
* subclass of +SignalException+.
*
* begin
@@ -1406,7 +2503,7 @@ syserr_eqq(VALUE self, VALUE exc)
*
* <em>raises the exception:</em>
*
- * TypeError: can't convert String into Integer
+ * TypeError: no implicit conversion of String into Integer
*
*/
@@ -1422,7 +2519,7 @@ syserr_eqq(VALUE self, VALUE exc)
*
* <em>raises the exception:</em>
*
- * ArgumentError: wrong number of arguments (2 for 1)
+ * ArgumentError: wrong number of arguments (given 2, expected 1)
*
* Ex: passing an argument that is not acceptable:
*
@@ -1530,7 +2627,7 @@ syserr_eqq(VALUE self, VALUE exc)
*
* Since constant names must start with a capital:
*
- * Fixnum.const_set :answer, 42
+ * Integer.const_set :answer, 42
*
* <em>raises the exception:</em>
*
@@ -1551,17 +2648,22 @@ syserr_eqq(VALUE self, VALUE exc)
*/
/*
- * Document-class: RuntimeError
+ * Document-class: FrozenError
*
- * A generic error class raised when an invalid operation is attempted.
+ * Raised when there is an attempt to modify a frozen object.
*
* [1, 2, 3].freeze << 4
*
* <em>raises the exception:</em>
*
- * RuntimeError: can't modify frozen array
+ * FrozenError: can't modify frozen Array
+ */
+
+/*
+ * Document-class: RuntimeError
*
- * Kernel.raise will raise a RuntimeError if no Exception class is
+ * A generic error class raised when an invalid operation is attempted.
+ * Kernel#raise will raise a RuntimeError if no Exception class is
* specified.
*
* raise "ouch"
@@ -1574,19 +2676,7 @@ syserr_eqq(VALUE self, VALUE exc)
/*
* Document-class: SecurityError
*
- * Raised when attempting a potential unsafe operation, typically when
- * the $SAFE level is raised above 0.
- *
- * foo = "bar"
- * proc = Proc.new do
- * $SAFE = 4
- * foo.gsub! "a", "*"
- * end
- * proc.call
- *
- * <em>raises the exception:</em>
- *
- * SecurityError: Insecure: can't modify string
+ * No longer used by internal code.
*/
/*
@@ -1627,8 +2717,8 @@ syserr_eqq(VALUE self, VALUE exc)
/*
* Document-class: fatal
*
- * fatal is an Exception that is raised when ruby has encountered a fatal
- * error and must exit. You are not able to rescue fatal.
+ * fatal is an Exception that is raised when Ruby has encountered a fatal
+ * error and must exit.
*/
/*
@@ -1637,32 +2727,48 @@ syserr_eqq(VALUE self, VALUE exc)
*/
/*
- * Descendants of class Exception are used to communicate between
+ * Document-class: Exception
+ *
+ * \Class Exception and its subclasses are used to communicate between
* Kernel#raise and +rescue+ statements in <code>begin ... end</code> blocks.
- * Exception objects carry information about the exception -- its type (the
- * exception's class name), an optional descriptive string, and optional
- * traceback information. Exception subclasses may add additional
- * information like NameError#name.
*
- * Programs may make subclasses of Exception, typically of StandardError or
- * RuntimeError, to provide custom classes and add additional information.
- * See the subclass list below for defaults for +raise+ and +rescue+.
+ * An Exception object carries information about an exception:
+ * - Its type (the exception's class).
+ * - An optional descriptive message.
+ * - Optional backtrace information.
+ *
+ * Some built-in subclasses of Exception have additional methods: e.g., NameError#name.
+ *
+ * == Defaults
+ *
+ * Two Ruby statements have default exception classes:
+ * - +raise+: defaults to RuntimeError.
+ * - +rescue+: defaults to StandardError.
+ *
+ * == Global Variables
*
* When an exception has been raised but not yet handled (in +rescue+,
- * +ensure+, +at_exit+ and +END+ blocks) the global variable <code>$!</code>
- * will contain the current exception and <code>$@</code> contains the
- * current exception's backtrace.
+ * +ensure+, +at_exit+ and +END+ blocks), two global variables are set:
+ * - <code>$!</code> contains the current exception.
+ * - <code>$@</code> contains its backtrace.
+ *
+ * == Custom Exceptions
+ *
+ * To provide additional or alternate information,
+ * a program may create custom exception classes
+ * that derive from the built-in exception classes.
*
- * It is recommended that a library should have one subclass of StandardError
- * or RuntimeError and have specific exception types inherit from it. This
- * allows the user to rescue a generic exception type to catch all exceptions
+ * A good practice is for a library to create a single "generic" exception class
+ * (typically a subclass of StandardError or RuntimeError)
+ * and have its other exception classes derive from that class.
+ * This allows the user to rescue the generic exception, thus catching all exceptions
* the library may raise even if future versions of the library add new
* exception subclasses.
*
* For example:
*
* class MyLibrary
- * class Error < RuntimeError
+ * class Error < ::StandardError
* end
*
* class WidgetError < Error
@@ -1673,8 +2779,10 @@ syserr_eqq(VALUE self, VALUE exc)
*
* end
*
- * To handle both WidgetError and FrobError the library user can rescue
- * MyLibrary::Error.
+ * To handle both MyLibrary::WidgetError and MyLibrary::FrobError the library
+ * user can rescue MyLibrary::Error.
+ *
+ * == Built-In Exception Classes
*
* The built-in subclasses of Exception are:
*
@@ -1683,45 +2791,105 @@ syserr_eqq(VALUE self, VALUE exc)
* * LoadError
* * NotImplementedError
* * SyntaxError
+ * * SecurityError
* * SignalException
* * Interrupt
- * * StandardError -- default for +rescue+
+ * * StandardError
* * ArgumentError
- * * IndexError
- * * StopIteration
+ * * UncaughtThrowError
+ * * EncodingError
+ * * FiberError
* * IOError
* * EOFError
+ * * IndexError
+ * * KeyError
+ * * StopIteration
+ * * ClosedQueueError
* * LocalJumpError
* * NameError
* * NoMethodError
* * RangeError
* * FloatDomainError
* * RegexpError
- * * RuntimeError -- default for +raise+
- * * SecurityError
+ * * RuntimeError
+ * * FrozenError
* * SystemCallError
* * Errno::*
- * * SystemStackError
* * ThreadError
* * TypeError
* * ZeroDivisionError
* * SystemExit
- * * fatal -- impossible to rescue
+ * * SystemStackError
+ * * fatal
*/
+static VALUE
+exception_alloc(VALUE klass)
+{
+ return rb_class_allocate_instance(klass);
+}
+
+static VALUE
+exception_dumper(VALUE exc)
+{
+ // TODO: Currently, the instance variables "bt" and "bt_locations"
+ // refers to the same object (Array of String). But "bt_locations"
+ // should have an Array of Thread::Backtrace::Locations.
+
+ return exc;
+}
+
+static int
+ivar_copy_i(st_data_t key, st_data_t val, st_data_t exc)
+{
+ rb_ivar_set((VALUE) exc, (ID) key, (VALUE) val);
+ return ST_CONTINUE;
+}
+
+void rb_exc_check_circular_cause(VALUE exc);
+
+static VALUE
+exception_loader(VALUE exc, VALUE obj)
+{
+ // The loader function of rb_marshal_define_compat seems to be called for two events:
+ // one is for fixup (r_fixup_compat), the other is for TYPE_USERDEF.
+ // In the former case, the first argument is an instance of Exception (because
+ // we pass rb_eException to rb_marshal_define_compat). In the latter case, the first
+ // argument is a class object (see TYPE_USERDEF case in r_object0).
+ // We want to copy all instance variables (but "bt_locations") from obj to exc.
+ // But we do not want to do so in the second case, so the following branch is for that.
+ if (RB_TYPE_P(exc, T_CLASS)) return obj; // maybe called from Marshal's TYPE_USERDEF
+
+ rb_ivar_foreach(obj, ivar_copy_i, exc);
+
+ rb_exc_check_circular_cause(exc);
+
+ if (rb_attr_get(exc, id_bt) == rb_attr_get(exc, id_bt_locations)) {
+ rb_ivar_set(exc, id_bt_locations, Qnil);
+ }
+
+ return exc;
+}
+
void
Init_Exception(void)
{
rb_eException = rb_define_class("Exception", rb_cObject);
+ rb_define_alloc_func(rb_eException, exception_alloc);
+ rb_marshal_define_compat(rb_eException, rb_eException, exception_dumper, exception_loader);
rb_define_singleton_method(rb_eException, "exception", rb_class_new_instance, -1);
+ rb_define_singleton_method(rb_eException, "to_tty?", exc_s_to_tty_p, 0);
rb_define_method(rb_eException, "exception", exc_exception, -1);
rb_define_method(rb_eException, "initialize", exc_initialize, -1);
rb_define_method(rb_eException, "==", exc_equal, 1);
rb_define_method(rb_eException, "to_s", exc_to_s, 0);
rb_define_method(rb_eException, "message", exc_message, 0);
+ rb_define_method(rb_eException, "full_message", exc_full_message, -1);
rb_define_method(rb_eException, "inspect", exc_inspect, 0);
rb_define_method(rb_eException, "backtrace", exc_backtrace, 0);
+ rb_define_method(rb_eException, "backtrace_locations", exc_backtrace_locations, 0);
rb_define_method(rb_eException, "set_backtrace", exc_set_backtrace, 1);
+ rb_define_method(rb_eException, "cause", exc_cause, 0);
rb_eSystemExit = rb_define_class("SystemExit", rb_eException);
rb_define_method(rb_eSystemExit, "initialize", exit_initialize, -1);
@@ -1737,35 +2905,51 @@ Init_Exception(void)
rb_eArgError = rb_define_class("ArgumentError", rb_eStandardError);
rb_eIndexError = rb_define_class("IndexError", rb_eStandardError);
rb_eKeyError = rb_define_class("KeyError", rb_eIndexError);
+ rb_define_method(rb_eKeyError, "initialize", key_err_initialize, -1);
+ rb_define_method(rb_eKeyError, "receiver", key_err_receiver, 0);
+ rb_define_method(rb_eKeyError, "key", key_err_key, 0);
rb_eRangeError = rb_define_class("RangeError", rb_eStandardError);
rb_eScriptError = rb_define_class("ScriptError", rb_eException);
rb_eSyntaxError = rb_define_class("SyntaxError", rb_eScriptError);
+ rb_define_method(rb_eSyntaxError, "initialize", syntax_error_initialize, -1);
rb_eLoadError = rb_define_class("LoadError", rb_eScriptError);
- rb_attr(rb_eLoadError, rb_intern("path"), 1, 0, Qfalse);
+ /* the path failed to load */
+ rb_attr(rb_eLoadError, rb_intern_const("path"), 1, 0, Qfalse);
rb_eNotImpError = rb_define_class("NotImplementedError", rb_eScriptError);
rb_eNameError = rb_define_class("NameError", rb_eStandardError);
rb_define_method(rb_eNameError, "initialize", name_err_initialize, -1);
rb_define_method(rb_eNameError, "name", name_err_name, 0);
- rb_define_method(rb_eNameError, "to_s", name_err_to_s, 0);
- rb_cNameErrorMesg = rb_define_class_under(rb_eNameError, "message", rb_cData);
- rb_define_singleton_method(rb_cNameErrorMesg, "!", rb_name_err_mesg_new, NAME_ERR_MESG_COUNT);
+ rb_define_method(rb_eNameError, "receiver", name_err_receiver, 0);
+ rb_define_method(rb_eNameError, "local_variables", name_err_local_variables, 0);
+ rb_cNameErrorMesg = rb_define_class_under(rb_eNameError, "message", rb_cObject);
+ rb_define_alloc_func(rb_cNameErrorMesg, name_err_mesg_alloc);
+ rb_define_method(rb_cNameErrorMesg, "initialize_copy", name_err_mesg_init_copy, 1);
rb_define_method(rb_cNameErrorMesg, "==", name_err_mesg_equal, 1);
rb_define_method(rb_cNameErrorMesg, "to_str", name_err_mesg_to_str, 0);
- rb_define_method(rb_cNameErrorMesg, "_dump", name_err_mesg_to_str, 1);
+ rb_define_method(rb_cNameErrorMesg, "_dump", name_err_mesg_dump, 1);
rb_define_singleton_method(rb_cNameErrorMesg, "_load", name_err_mesg_load, 1);
rb_eNoMethodError = rb_define_class("NoMethodError", rb_eNameError);
rb_define_method(rb_eNoMethodError, "initialize", nometh_err_initialize, -1);
rb_define_method(rb_eNoMethodError, "args", nometh_err_args, 0);
+ rb_define_method(rb_eNoMethodError, "private_call?", nometh_err_private_call_p, 0);
rb_eRuntimeError = rb_define_class("RuntimeError", rb_eStandardError);
+ rb_eFrozenError = rb_define_class("FrozenError", rb_eRuntimeError);
+ rb_define_method(rb_eFrozenError, "initialize", frozen_err_initialize, -1);
+ rb_define_method(rb_eFrozenError, "receiver", frozen_err_receiver, 0);
rb_eSecurityError = rb_define_class("SecurityError", rb_eException);
rb_eNoMemError = rb_define_class("NoMemoryError", rb_eException);
rb_eEncodingError = rb_define_class("EncodingError", rb_eStandardError);
rb_eEncCompatError = rb_define_class_under(rb_cEncoding, "CompatibilityError", rb_eEncodingError);
+ rb_eNoMatchingPatternError = rb_define_class("NoMatchingPatternError", rb_eStandardError);
+ rb_eNoMatchingPatternKeyError = rb_define_class("NoMatchingPatternKeyError", rb_eNoMatchingPatternError);
+ rb_define_method(rb_eNoMatchingPatternKeyError, "initialize", no_matching_pattern_key_err_initialize, -1);
+ rb_define_method(rb_eNoMatchingPatternKeyError, "matchee", no_matching_pattern_key_err_matchee, 0);
+ rb_define_method(rb_eNoMatchingPatternKeyError, "key", no_matching_pattern_key_err_key, 0);
syserr_tbl = st_init_numtable();
rb_eSystemCallError = rb_define_class("SystemCallError", rb_eStandardError);
@@ -1775,7 +2959,47 @@ Init_Exception(void)
rb_mErrno = rb_define_module("Errno");
- rb_define_global_function("warn", rb_warn_m, -1);
+ rb_mWarning = rb_define_module("Warning");
+ rb_define_singleton_method(rb_mWarning, "[]", rb_warning_s_aref, 1);
+ rb_define_singleton_method(rb_mWarning, "[]=", rb_warning_s_aset, 2);
+ rb_define_method(rb_mWarning, "warn", rb_warning_s_warn, -1);
+ rb_extend_object(rb_mWarning, rb_mWarning);
+
+ /* :nodoc: */
+ rb_cWarningBuffer = rb_define_class_under(rb_mWarning, "buffer", rb_cString);
+ rb_define_method(rb_cWarningBuffer, "write", warning_write, -1);
+
+ id_cause = rb_intern_const("cause");
+ id_message = rb_intern_const("message");
+ id_backtrace = rb_intern_const("backtrace");
+ id_key = rb_intern_const("key");
+ id_matchee = rb_intern_const("matchee");
+ id_args = rb_intern_const("args");
+ id_receiver = rb_intern_const("receiver");
+ id_private_call_p = rb_intern_const("private_call?");
+ id_local_variables = rb_intern_const("local_variables");
+ id_Errno = rb_intern_const("Errno");
+ id_errno = rb_intern_const("errno");
+ id_i_path = rb_intern_const("@path");
+ id_warn = rb_intern_const("warn");
+ id_category = rb_intern_const("category");
+ id_deprecated = rb_intern_const("deprecated");
+ id_experimental = rb_intern_const("experimental");
+ id_top = rb_intern_const("top");
+ id_bottom = rb_intern_const("bottom");
+ id_iseq = rb_make_internal_id();
+ id_recv = rb_make_internal_id();
+
+ sym_category = ID2SYM(id_category);
+
+ warning_categories.id2enum = rb_init_identtable();
+ st_add_direct(warning_categories.id2enum, id_deprecated, RB_WARN_CATEGORY_DEPRECATED);
+ st_add_direct(warning_categories.id2enum, id_experimental, RB_WARN_CATEGORY_EXPERIMENTAL);
+
+ warning_categories.enum2id = rb_init_identtable();
+ st_add_direct(warning_categories.enum2id, RB_WARN_CATEGORY_NONE, 0);
+ st_add_direct(warning_categories.enum2id, RB_WARN_CATEGORY_DEPRECATED, id_deprecated);
+ st_add_direct(warning_categories.enum2id, RB_WARN_CATEGORY_EXPERIMENTAL, id_experimental);
}
void
@@ -1792,15 +3016,18 @@ rb_enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...)
}
void
+rb_vraise(VALUE exc, const char *fmt, va_list ap)
+{
+ rb_exc_raise(rb_exc_new3(exc, rb_vsprintf(fmt, ap)));
+}
+
+void
rb_raise(VALUE exc, const char *fmt, ...)
{
va_list args;
- VALUE mesg;
-
va_start(args, fmt);
- mesg = rb_vsprintf(fmt, args);
+ rb_vraise(exc, fmt, args);
va_end(args);
- rb_exc_raise(rb_exc_new3(exc, mesg));
}
NORETURN(static void raise_loaderror(VALUE path, VALUE mesg));
@@ -1809,7 +3036,7 @@ static void
raise_loaderror(VALUE path, VALUE mesg)
{
VALUE err = rb_exc_new3(rb_eLoadError, mesg);
- rb_ivar_set(err, rb_intern("@path"), path);
+ rb_ivar_set(err, id_i_path, path);
rb_exc_raise(err);
}
@@ -1841,8 +3068,8 @@ void
rb_notimplement(void)
{
rb_raise(rb_eNotImpError,
- "%s() function is unimplemented on this machine",
- rb_id2name(rb_frame_this_func()));
+ "%"PRIsVALUE"() function is unimplemented on this machine",
+ rb_id2str(rb_frame_this_func()));
}
void
@@ -1851,6 +3078,14 @@ rb_fatal(const char *fmt, ...)
va_list args;
VALUE mesg;
+ if (! ruby_thread_has_gvl_p()) {
+ /* The thread has no GVL. Object allocation impossible (cant run GC),
+ * thus no message can be printed out. */
+ fprintf(stderr, "[FATAL] rb_fatal() outside of GVL\n");
+ rb_print_backtrace();
+ die();
+ }
+
va_start(args, fmt);
mesg = rb_vsprintf(fmt, args);
va_end(args);
@@ -1922,56 +3157,175 @@ rb_sys_fail_str(VALUE mesg)
rb_exc_raise(make_errno_exc_str(mesg));
}
+#ifdef RUBY_FUNCTION_NAME_STRING
void
-rb_mod_sys_fail(VALUE mod, const char *mesg)
+rb_sys_fail_path_in(const char *func_name, VALUE path)
+{
+ int n = errno;
+
+ errno = 0;
+ rb_syserr_fail_path_in(func_name, n, path);
+}
+
+void
+rb_syserr_fail_path_in(const char *func_name, int n, VALUE path)
+{
+ rb_exc_raise(rb_syserr_new_path_in(func_name, n, path));
+}
+
+VALUE
+rb_syserr_new_path_in(const char *func_name, int n, VALUE path)
+{
+ VALUE args[2];
+
+ if (!path) path = Qnil;
+ if (n == 0) {
+ const char *s = !NIL_P(path) ? RSTRING_PTR(path) : "";
+ if (!func_name) func_name = "(null)";
+ rb_bug("rb_sys_fail_path_in(%s, %s) - errno == 0",
+ func_name, s);
+ }
+ args[0] = path;
+ args[1] = rb_str_new_cstr(func_name);
+ return rb_class_new_instance(2, args, get_syserr(n));
+}
+#endif
+
+NORETURN(static void rb_mod_exc_raise(VALUE exc, VALUE mod));
+
+static void
+rb_mod_exc_raise(VALUE exc, VALUE mod)
{
- VALUE exc = make_errno_exc(mesg);
rb_extend_object(exc, mod);
rb_exc_raise(exc);
}
void
+rb_mod_sys_fail(VALUE mod, const char *mesg)
+{
+ VALUE exc = make_errno_exc(mesg);
+ rb_mod_exc_raise(exc, mod);
+}
+
+void
rb_mod_sys_fail_str(VALUE mod, VALUE mesg)
{
VALUE exc = make_errno_exc_str(mesg);
- rb_extend_object(exc, mod);
- rb_exc_raise(exc);
+ rb_mod_exc_raise(exc, mod);
}
void
rb_mod_syserr_fail(VALUE mod, int e, const char *mesg)
{
VALUE exc = rb_syserr_new(e, mesg);
- rb_extend_object(exc, mod);
- rb_exc_raise(exc);
+ rb_mod_exc_raise(exc, mod);
}
void
rb_mod_syserr_fail_str(VALUE mod, int e, VALUE mesg)
{
VALUE exc = rb_syserr_new_str(e, mesg);
- rb_extend_object(exc, mod);
- rb_exc_raise(exc);
+ rb_mod_exc_raise(exc, mod);
+}
+
+static void
+syserr_warning(VALUE mesg, int err)
+{
+ rb_str_set_len(mesg, RSTRING_LEN(mesg)-1);
+ rb_str_catf(mesg, ": %s\n", strerror(err));
+ rb_write_warning_str(mesg);
}
+#if 0
void
-rb_sys_warning(const char *fmt, ...)
+rb_sys_warn(const char *fmt, ...)
{
- char buf[BUFSIZ];
- va_list args;
- int errno_save;
+ if (!NIL_P(ruby_verbose)) {
+ int errno_save = errno;
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, errno_save);
+ }
+ errno = errno_save;
+ }
+}
+
+void
+rb_syserr_warn(int err, const char *fmt, ...)
+{
+ if (!NIL_P(ruby_verbose)) {
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, err);
+ }
+ }
+}
- errno_save = errno;
+void
+rb_sys_enc_warn(rb_encoding *enc, const char *fmt, ...)
+{
+ if (!NIL_P(ruby_verbose)) {
+ int errno_save = errno;
+ with_warning_string(mesg, enc, fmt) {
+ syserr_warning(mesg, errno_save);
+ }
+ errno = errno_save;
+ }
+}
- if (!RTEST(ruby_verbose)) return;
+void
+rb_syserr_enc_warn(int err, rb_encoding *enc, const char *fmt, ...)
+{
+ if (!NIL_P(ruby_verbose)) {
+ with_warning_string(mesg, enc, fmt) {
+ syserr_warning(mesg, err);
+ }
+ }
+}
+#endif
- snprintf(buf, BUFSIZ, "warning: %s", fmt);
- snprintf(buf+strlen(buf), BUFSIZ-strlen(buf), ": %s", strerror(errno_save));
+void
+rb_sys_warning(const char *fmt, ...)
+{
+ if (RTEST(ruby_verbose)) {
+ int errno_save = errno;
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, errno_save);
+ }
+ errno = errno_save;
+ }
+}
- va_start(args, fmt);
- warn_print(buf, args);
- va_end(args);
- errno = errno_save;
+#if 0
+void
+rb_syserr_warning(int err, const char *fmt, ...)
+{
+ if (RTEST(ruby_verbose)) {
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, err);
+ }
+ }
+}
+#endif
+
+void
+rb_sys_enc_warning(rb_encoding *enc, const char *fmt, ...)
+{
+ if (RTEST(ruby_verbose)) {
+ int errno_save = errno;
+ with_warning_string(mesg, enc, fmt) {
+ syserr_warning(mesg, errno_save);
+ }
+ errno = errno_save;
+ }
+}
+
+void
+rb_syserr_enc_warning(int err, rb_encoding *enc, const char *fmt, ...)
+{
+ if (RTEST(ruby_verbose)) {
+ with_warning_string(mesg, enc, fmt) {
+ syserr_warning(mesg, err);
+ }
+ }
}
void
@@ -1986,7 +3340,54 @@ rb_load_fail(VALUE path, const char *err)
void
rb_error_frozen(const char *what)
{
- rb_raise(rb_eRuntimeError, "can't modify frozen %s", what);
+ rb_raise(rb_eFrozenError, "can't modify frozen %s", what);
+}
+
+void
+rb_frozen_error_raise(VALUE frozen_obj, const char *fmt, ...)
+{
+ va_list args;
+ VALUE exc, mesg;
+
+ va_start(args, fmt);
+ mesg = rb_vsprintf(fmt, args);
+ va_end(args);
+ exc = rb_exc_new3(rb_eFrozenError, mesg);
+ rb_ivar_set(exc, id_recv, frozen_obj);
+ rb_exc_raise(exc);
+}
+
+static VALUE
+inspect_frozen_obj(VALUE obj, VALUE mesg, int recur)
+{
+ if (recur) {
+ rb_str_cat_cstr(mesg, " ...");
+ }
+ else {
+ rb_str_append(mesg, rb_inspect(obj));
+ }
+ return mesg;
+}
+
+void
+rb_error_frozen_object(VALUE frozen_obj)
+{
+ VALUE debug_info;
+ const ID created_info = id_debug_created_info;
+ VALUE mesg = rb_sprintf("can't modify frozen %"PRIsVALUE": ",
+ CLASS_OF(frozen_obj));
+ VALUE exc = rb_exc_new_str(rb_eFrozenError, mesg);
+
+ rb_ivar_set(exc, id_recv, frozen_obj);
+ rb_exec_recursive(inspect_frozen_obj, frozen_obj, mesg);
+
+ if (!NIL_P(debug_info = rb_attr_get(frozen_obj, created_info))) {
+ VALUE path = rb_ary_entry(debug_info, 0);
+ VALUE line = rb_ary_entry(debug_info, 1);
+
+ rb_str_catf(mesg, ", created at %"PRIsVALUE":%"PRIsVALUE, path, line);
+ }
+ rb_exc_raise(exc);
}
#undef rb_check_frozen
@@ -1999,17 +3400,14 @@ rb_check_frozen(VALUE obj)
void
rb_error_untrusted(VALUE obj)
{
- if (rb_safe_level() >= 4) {
- rb_raise(rb_eSecurityError, "Insecure: can't modify %s",
- rb_obj_classname(obj));
- }
+ rb_warn_deprecated_to_remove_at(3.2, "rb_error_untrusted", NULL);
}
#undef rb_check_trusted
void
rb_check_trusted(VALUE obj)
{
- rb_check_trusted_internal(obj);
+ rb_warn_deprecated_to_remove_at(3.2, "rb_check_trusted", NULL);
}
void
@@ -2017,14 +3415,7 @@ rb_check_copyable(VALUE obj, VALUE orig)
{
if (!FL_ABLE(obj)) return;
rb_check_frozen_internal(obj);
- rb_check_trusted_internal(obj);
if (!FL_ABLE(orig)) return;
- if ((~RBASIC(obj)->flags & RBASIC(orig)->flags) & (FL_UNTRUSTED|FL_TAINT)) {
- if (rb_safe_level() > 0) {
- rb_raise(rb_eSecurityError, "Insecure: can't modify %"PRIsVALUE,
- RBASIC(obj)->klass);
- }
- }
}
void
@@ -2037,3 +3428,9 @@ Init_syserr(void)
#undef defined_error
#undef undefined_error
}
+
+#include "warning.rbinc"
+
+/*!
+ * \}
+ */