summaryrefslogtreecommitdiff
path: root/error.c
diff options
context:
space:
mode:
Diffstat (limited to 'error.c')
-rw-r--r--error.c2047
1 files changed, 1618 insertions, 429 deletions
diff --git a/error.c b/error.c
index 6b13c234b9..65ad20a27b 100644
--- a/error.c
+++ b/error.c
@@ -9,20 +9,46 @@
**********************************************************************/
-#include "internal.h"
-#include "ruby/st.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
+
+#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
#endif
@@ -35,24 +61,28 @@
#define WEXITSTATUS(status) (status)
#endif
+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;
extern const char ruby_description[];
-static const char REPORTBUG_MSG[] =
- "[NOTE]\n" \
- "You may have encountered a bug in the Ruby interpreter" \
- " or extension libraries.\n" \
- "Bug reports are welcome.\n" \
- ""
-#if defined __APPLE__
- "Don't forget to include the above Crash Report log file.\n"
-#endif
- "For details: http://www.ruby-lang.org/bugreport.html\n\n" \
- ;
-
static const char *
rb_strerrno(int err)
{
@@ -78,11 +108,11 @@ err_position_0(char *buf, long len, const char *file, int line)
}
}
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 5, 0)
static VALUE
-compile_snprintf(rb_encoding *enc, const char *pre, const char *file, int line, const char *fmt, va_list args)
+err_vcatf(VALUE str, const char *pre, const char *file, int line,
+ const char *fmt, va_list args)
{
- VALUE str = rb_enc_str_new(0, 0, enc);
-
if (file) {
rb_str_cat2(str, file);
if (line) rb_str_catf(str, ":%d", line);
@@ -93,212 +123,485 @@ compile_snprintf(rb_encoding *enc, const char *pre, const char *file, int line,
return str;
}
-static void
-compile_err_append(VALUE mesg)
-{
- 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)) {
- VALUE str = rb_obj_as_string(err);
-
- rb_str_cat2(str, "\n");
- rb_str_append(str, mesg);
- mesg = str;
- }
- err = rb_exc_new3(rb_eSyntaxError, mesg);
- th->errinfo = err;
+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 {
- if (!RTEST(err)) {
- err = rb_exc_new2(rb_eSyntaxError, "compile error");
- th->errinfo = err;
+ VALUE mesg;
+ if (NIL_P(exc)) {
+ mesg = rb_enc_str_new(0, 0, enc);
+ exc = rb_class_new_instance(1, &mesg, rb_eSyntaxError);
}
- rb_str_cat2(mesg, "\n");
- rb_write_error_str(mesg);
+ 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);
}
- /* returned to the parser world */
- th->base_block = prev_base_block;
+ return exc;
}
-void
-rb_compile_error_with_enc(const char *file, int line, void *enc, const char *fmt, ...)
+static unsigned int warning_disabled_categories = (
+ 1U << RB_WARN_CATEGORY_DEPRECATED |
+ 0);
+
+static unsigned int
+rb_warning_category_mask(VALUE category)
{
- va_list args;
- VALUE str;
+ return 1U << rb_warning_category_from_name(category);
+}
- va_start(args, fmt);
- str = compile_snprintf(enc, NULL, file, line, fmt, args);
- va_end(args);
- compile_err_append(str);
+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 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_compile_error(const char *file, int line, const char *fmt, ...)
+rb_warning_category_update(unsigned int mask, unsigned int bits)
{
- va_list args;
- VALUE str;
+ warning_disabled_categories &= ~mask;
+ warning_disabled_categories |= mask & ~bits;
+}
- va_start(args, fmt);
- str = compile_snprintf(NULL, NULL, file, line, fmt, args);
- va_end(args);
- compile_err_append(str);
+MJIT_FUNC_EXPORTED bool
+rb_warning_category_enabled_p(rb_warning_category_t category)
+{
+ return !(warning_disabled_categories & (1U << category));
}
-void
-rb_compile_error_append(const char *fmt, ...)
+/*
+ * 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)
{
- va_list args;
- VALUE str;
+ rb_warning_category_t cat = rb_warning_category_from_name(category);
+ return RBOOL(rb_warning_category_enabled_p(cat));
+}
- va_start(args, fmt);
- str = rb_vsprintf(fmt, args);
- va_end(args);
- compile_err_append(str);
+/*
+ * 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;
}
-static void
-compile_warn_print(const char *file, int line, const char *fmt, va_list args)
+/*
+ * 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);
- str = compile_snprintf(NULL, "warning: ", file, line, fmt, args);
- rb_str_cat2(str, "\n");
+ 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;
+}
+
+/*
+ * 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.
+ */
+
+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 {
+ 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);
+
+ err_vcatf(str, "warning: ", file, line, fmt, args);
+ return rb_str_cat2(str, "\n");
}
void
rb_compile_warn(const char *file, int line, const char *fmt, ...)
{
+ VALUE str;
va_list args;
if (NIL_P(ruby_verbose)) return;
va_start(args, fmt);
- compile_warn_print(file, line, fmt, args);
+ str = warn_vsprintf(NULL, file, line, fmt, args);
va_end(args);
+ rb_write_warning_str(str);
}
/* rb_compile_warning() reports only in verbose mode */
void
rb_compile_warning(const char *file, int line, const char *fmt, ...)
{
+ VALUE str;
va_list args;
if (!RTEST(ruby_verbose)) return;
va_start(args, fmt);
- compile_warn_print(file, line, fmt, args);
+ str = warn_vsprintf(NULL, file, line, fmt, args);
va_end(args);
-}
-
-static VALUE
-warning_string(rb_encoding *enc, const char *fmt, va_list args)
-{
- VALUE str = rb_enc_str_new(0, 0, enc);
- VALUE file = rb_sourcefilename();
-
- if (!NIL_P(file)) {
- int line = rb_sourceline();
- str = rb_str_append(str, file);
- if (line) rb_str_catf(str, ":%d", line);
- rb_str_cat2(str, ": ");
- }
-
- rb_str_cat2(str, "warning: ");
- rb_str_vcatf(str, fmt, args);
- rb_str_cat2(str, "\n");
- return str;
+ rb_write_warning_str(str);
}
void
-rb_warn(const char *fmt, ...)
+rb_category_compile_warn(rb_warning_category_t category, const char *file, int line, const char *fmt, ...)
{
- VALUE mesg;
+ VALUE str;
va_list args;
if (NIL_P(ruby_verbose)) return;
va_start(args, fmt);
- mesg = warning_string(0, fmt, args);
+ str = warn_vsprintf(NULL, file, line, fmt, args);
va_end(args);
- rb_write_error_str(mesg);
+ rb_warn_category(str, rb_warning_category_to_name(category));
}
-#if 0
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 0)
+static VALUE
+warning_string(rb_encoding *enc, const char *fmt, va_list args)
+{
+ 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_enc_warn(rb_encoding *enc, const char *fmt, ...)
+rb_warn(const char *fmt, ...)
{
- VALUE mesg;
- 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));
+ }
+ }
+}
- va_start(args, fmt);
- mesg = warning_string(enc, fmt, args);
- va_end(args);
- rb_write_error_str(mesg);
+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);
+ }
+ }
}
-#endif
/* rb_warning() reports only in verbose mode */
void
rb_warning(const char *fmt, ...)
{
- VALUE mesg;
- va_list args;
+ if (RTEST(ruby_verbose)) {
+ with_warning_string(mesg, 0, fmt) {
+ rb_write_warning_str(mesg);
+ }
+ }
+}
- if (!RTEST(ruby_verbose)) return;
+/* rb_category_warning() reports only in verbose mode */
+void
+rb_category_warning(rb_warning_category_t category, const char *fmt, ...)
+{
+ if (RTEST(ruby_verbose)) {
+ with_warning_string(mesg, 0, fmt) {
+ rb_warn_category(mesg, rb_warning_category_to_name(category));
+ }
+ }
+}
- va_start(args, fmt);
- mesg = warning_string(0, fmt, args);
- va_end(args);
- rb_write_error_str(mesg);
+VALUE
+rb_warning_string(const char *fmt, ...)
+{
+ with_warning_string(mesg, 0, fmt) {
+ }
+ return mesg;
}
#if 0
void
rb_enc_warning(rb_encoding *enc, const char *fmt, ...)
{
- VALUE mesg;
+ if (RTEST(ruby_verbose)) {
+ with_warning_string(mesg, enc, fmt) {
+ rb_write_warning_str(mesg);
+ }
+ }
+}
+#endif
+
+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_deprecated(VALUE mesg, const char *removal, const char *suggest)
+{
+ 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_deprecated(const char *fmt, const char *suggest, ...)
+{
+ 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, NULL, suggest);
+}
- va_start(args, fmt);
- mesg = warning_string(enc, fmt, args);
+void
+rb_warn_deprecated_to_remove(const char *removal, const char *fmt, const char *suggest, ...)
+{
+ if (!deprecation_warning_enabled()) return;
+
+ va_list args;
+ va_start(args, suggest);
+ VALUE mesg = warning_string(0, fmt, args);
va_end(args);
- rb_write_error_str(mesg);
+
+ warn_deprecated(mesg, removal, suggest);
}
-#endif
-/*
- * 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
- */
+static inline int
+end_with_asciichar(VALUE str, int c)
+{
+ return RB_TYPE_P(str, T_STRING) &&
+ rb_str_end_with_asciichar(str, c);
+}
+/* :nodoc: */
static VALUE
-rb_warn_m(int argc, VALUE *argv, VALUE exc)
+warning_write(int argc, VALUE *argv, VALUE buf)
{
+ while (argc-- > 0) {
+ rb_str_append(buf, *argv++);
+ }
+ return buf;
+}
+
+VALUE rb_ec_backtrace_location_ary(const rb_execution_context_t *ec, long lev, long n, bool skip_internal);
+
+static VALUE
+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;
}
@@ -337,13 +640,89 @@ bug_report_file(const char *file, int 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;
+ return 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(FILE *out, const char *fmt, va_list args)
+bug_report_begin_valist(FILE *out, const char *fmt, va_list args)
{
char buf[REPORT_BUG_BUFSIZ];
@@ -352,12 +731,13 @@ bug_report_begin(FILE *out, const char *fmt, va_list 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(out, fmt, args); \
+ bug_report_begin_valist(out, fmt, args); \
va_end(args); \
} while (0)
@@ -372,7 +752,7 @@ bug_report_end(FILE *out)
(*reporter->func)(out, reporter->data);
}
}
- fprintf(out, REPORTBUG_MSG);
+ postscript_dump(out);
}
#define report_bug(file, line, fmt, ctx) do { \
@@ -384,6 +764,15 @@ bug_report_end(FILE *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)
@@ -395,35 +784,44 @@ die(void)
abort();
}
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 0)
void
-rb_bug(const char *fmt, ...)
+rb_bug_without_die(const char *fmt, va_list args)
{
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);
}
- report_bug(file, line, fmt, NULL);
+ 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_context(const void *ctx, const char *fmt, ...)
+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);
}
report_bug(file, line, fmt, ctx);
+ if (default_sighandler) default_sighandler(sig);
+
die();
}
@@ -468,17 +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)
{
- report_bug(file, line, fmt, NULL);
+ 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] = {
@@ -492,7 +899,7 @@ static const char builtin_types[][10] = {
"Array",
"Hash",
"Struct",
- "Bignum",
+ "Integer",
"File",
"Data", /* internal use: wrapped C pointers */
"MatchData", /* data of $~ */
@@ -503,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 *
@@ -524,9 +930,19 @@ rb_builtin_type_name(int t)
return 0;
}
-#define builtin_class_name rb_builtin_class_name
-const char *
-rb_builtin_class_name(VALUE x)
+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)
{
const char *etype;
@@ -534,7 +950,7 @@ rb_builtin_class_name(VALUE x)
etype = "nil";
}
else if (FIXNUM_P(x)) {
- etype = "Fixnum";
+ etype = "Integer";
}
else if (SYMBOL_P(x)) {
etype = "Symbol";
@@ -546,32 +962,77 @@ rb_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
@@ -594,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... */
+ }
+ else {
+ return DATA_PTR(obj);
}
- 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 */
@@ -623,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;
@@ -636,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;
@@ -645,12 +1120,24 @@ VALUE rb_eSystemCallError;
VALUE rb_mErrno;
static VALUE rb_eNOERROR;
+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
@@ -663,12 +1150,22 @@ VALUE
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.
@@ -679,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
@@ -704,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;
}
@@ -723,33 +1217,118 @@ 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 mesg = rb_attr_get(exc, idMesg);
if (NIL_P(mesg)) return rb_class_name(CLASS_OF(exc));
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;
+}
+
/*
* call-seq:
* 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
@@ -760,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("#<");
@@ -775,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
@@ -800,70 +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;
- CONST_ID(mid, "backtrace");
- if (rb_method_basic_definition_p(CLASS_OF(exc), mid)) {
- VALUE info, klass = rb_eException;
- rb_thread_t *th = GET_THREAD();
+ 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(th, RUBY_EVENT_C_CALL, exc, mid, klass, Qundef);
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_CALL, exc, mid, mid, klass, Qundef);
info = exc_backtrace(exc);
- EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, exc, mid, klass, info);
- if (NIL_P(info))
- return Qnil;
- return rb_check_backtrace(info);
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, exc, mid, mid, klass, info);
+ }
+ else {
+ info = rb_funcallv(exc, mid, 0, 0);
}
- return rb_funcall(exc, mid, 0, 0);
+ if (NIL_P(info)) return Qnil;
+ return rb_check_backtrace(info);
}
/*
* call-seq:
- * exception.backtrace_locations -> array
+ * 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.
+ * Thread::Backtrace::Location.
*
- * Now, this method is not affected by Exception#set_backtrace().
+ * This method is not affected by Exception#set_backtrace().
*/
static VALUE
exc_backtrace_locations(VALUE exc)
{
- ID bt_locations;
VALUE obj;
- CONST_ID(bt_locations, "bt_locations");
- obj = rb_attr_get(exc, bt_locations);
+ obj = rb_attr_get(exc, id_bt_locations);
if (!NIL_P(obj)) {
obj = rb_backtrace_to_location_ary(obj);
}
return obj;
}
-VALUE
+static VALUE
rb_check_backtrace(VALUE bt)
{
long i;
@@ -898,10 +1482,10 @@ 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);
@@ -916,11 +1500,9 @@ rb_exc_set_backtrace(VALUE exc, VALUE bt)
* information.
*/
-VALUE
+static VALUE
exc_cause(VALUE exc)
{
- ID id_cause;
- CONST_ID(id_cause, "cause");
return rb_attr_get(exc, id_cause);
}
@@ -934,7 +1516,7 @@ try_convert_to_exception(VALUE obj)
* 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.
*/
@@ -943,18 +1525,14 @@ static VALUE
exc_equal(VALUE exc, VALUE obj)
{
VALUE mesg, backtrace;
- const ID id_mesg = idMesg;
if (exc == obj) return Qtrue;
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;
}
@@ -971,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);
}
/*
@@ -1026,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.
*/
@@ -1041,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);
}
@@ -1055,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, ...)
{
@@ -1097,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
@@ -1127,30 +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.local_variables -> array
+ *
+ * Return a list of the local variable names defined where this
+ * NameError exception was raised.
+ *
+ * Internal use only.
+ */
+
+static VALUE
+name_err_local_variables(VALUE self)
+{
+ VALUE vars = rb_attr_get(self, id_local_variables);
+
+ 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)
@@ -1164,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 = {
@@ -1178,24 +1885,50 @@ static const rb_data_type_t name_err_mesg_data_type = {
};
/* :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;
@@ -1216,48 +1949,68 @@ 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_append(d, rb_class_name(CLASS_OF(obj)));
+ if (!singleton) {
+ s = FAKE_CSTR(&s_str, ":");
+ c = rb_class_name(CLASS_OF(obj));
+ }
+ else {
+ c = s = FAKE_CSTR(&s_str, "");
}
- args[0] = mesg;
- args[1] = ptr[2];
- args[2] = d;
- mesg = rb_f_sprintf(NAME_ERR_MESG_COUNT, args);
+ 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;
}
@@ -1278,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
@@ -1287,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
@@ -1299,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
@@ -1324,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, ...
*/
@@ -1383,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
@@ -1417,7 +2367,7 @@ syserr_initialize(int argc, VALUE *argv, VALUE self)
}
else {
rb_scan_args(argc, argv, "02", &mesg, &func);
- error = rb_const_get(klass, rb_intern("Errno"));
+ error = rb_const_get(klass, id_Errno);
}
if (!NIL_P(error)) err = strerror(NUM2INT(error));
else err = "unknown error";
@@ -1428,18 +2378,17 @@ syserr_initialize(int argc, VALUE *argv, VALUE self)
if (!NIL_P(func)) rb_str_catf(errmsg, " @ %"PRIsVALUE, func);
rb_str_catf(errmsg, " - %"PRIsVALUE, str);
- OBJ_INFECT(errmsg, mesg);
}
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.
*/
@@ -1447,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);
}
/*
@@ -1462,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));
}
@@ -1530,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
@@ -1575,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:
*
@@ -1683,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>
*
@@ -1704,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"
@@ -1727,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 = 3
- * foo.untaint
- * end
- * proc.call
- *
- * <em>raises the exception:</em>
- *
- * SecurityError: Insecure: Insecure operation `untaint' at level 3
+ * No longer used by internal code.
*/
/*
@@ -1780,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.
*/
/*
@@ -1790,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.
*
- * 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
+ * == Custom Exceptions
+ *
+ * To provide additional or alternate information,
+ * a program may create custom exception classes
+ * that derive from the built-in exception classes.
+ *
+ * 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
@@ -1826,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:
*
@@ -1839,7 +2794,7 @@ syserr_eqq(VALUE self, VALUE exc)
* * SecurityError
* * SignalException
* * Interrupt
- * * StandardError -- default for +rescue+
+ * * StandardError
* * ArgumentError
* * UncaughtThrowError
* * EncodingError
@@ -1849,13 +2804,15 @@ syserr_eqq(VALUE self, VALUE exc)
* * IndexError
* * KeyError
* * StopIteration
+ * * ClosedQueueError
* * LocalJumpError
* * NameError
* * NoMethodError
* * RangeError
* * FloatDomainError
* * RegexpError
- * * RuntimeError -- default for +raise+
+ * * RuntimeError
+ * * FrozenError
* * SystemCallError
* * Errno::*
* * ThreadError
@@ -1863,19 +2820,71 @@ syserr_eqq(VALUE self, VALUE exc)
* * ZeroDivisionError
* * SystemExit
* * SystemStackError
- * * fatal -- impossible to rescue
+ * * 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);
@@ -1896,10 +2905,14 @@ 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);
/* the path failed to load */
@@ -1910,8 +2923,11 @@ Init_Exception(void)
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_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_dump, 1);
@@ -1919,12 +2935,21 @@ Init_Exception(void)
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);
@@ -1934,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
@@ -1951,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));
@@ -1968,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);
}
@@ -2010,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);
@@ -2094,6 +3170,12 @@ rb_sys_fail_path_in(const char *func_name, VALUE 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;
@@ -2105,80 +3187,145 @@ rb_syserr_fail_path_in(const char *func_name, int n, VALUE path)
}
args[0] = path;
args[1] = rb_str_new_cstr(func_name);
- rb_exc_raise(rb_class_new_instance(2, args, get_syserr(n)));
+ 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)
+{
+ 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_extend_object(exc, mod);
- rb_exc_raise(exc);
+ 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, ...)
{
- VALUE mesg;
- 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;
+ }
+}
- errno_save = errno;
+void
+rb_syserr_warn(int err, const char *fmt, ...)
+{
+ if (!NIL_P(ruby_verbose)) {
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, err);
+ }
+ }
+}
- if (!RTEST(ruby_verbose)) return;
+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;
+ }
+}
- va_start(args, fmt);
- mesg = warning_string(0, fmt, args);
- va_end(args);
- rb_str_set_len(mesg, RSTRING_LEN(mesg)-1);
- rb_str_catf(mesg, ": %s\n", strerror(errno_save));
- rb_write_error_str(mesg);
- errno = errno_save;
+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
void
-rb_sys_enc_warning(rb_encoding *enc, const char *fmt, ...)
+rb_sys_warning(const char *fmt, ...)
{
- VALUE mesg;
- va_list args;
- int errno_save;
+ if (RTEST(ruby_verbose)) {
+ int errno_save = errno;
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, errno_save);
+ }
+ errno = errno_save;
+ }
+}
- errno_save = errno;
+#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
- if (!RTEST(ruby_verbose)) return;
+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;
+ }
+}
- va_start(args, fmt);
- mesg = warning_string(enc, fmt, args);
- va_end(args);
- rb_str_set_len(mesg, RSTRING_LEN(mesg)-1);
- rb_str_catf(mesg, ": %s\n", strerror(errno_save));
- rb_write_error_str(mesg);
- 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
@@ -2193,14 +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)
{
- rb_raise(rb_eRuntimeError, "can't modify frozen %"PRIsVALUE,
- CLASS_OF(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
@@ -2213,12 +3400,14 @@ rb_check_frozen(VALUE obj)
void
rb_error_untrusted(VALUE obj)
{
+ rb_warn_deprecated_to_remove_at(3.2, "rb_error_untrusted", NULL);
}
#undef rb_check_trusted
void
rb_check_trusted(VALUE obj)
{
+ rb_warn_deprecated_to_remove_at(3.2, "rb_check_trusted", NULL);
}
void
@@ -2227,12 +3416,6 @@ rb_check_copyable(VALUE obj, VALUE orig)
if (!FL_ABLE(obj)) return;
rb_check_frozen_internal(obj);
if (!FL_ABLE(orig)) return;
- if ((~RBASIC(obj)->flags & RBASIC(orig)->flags) & FL_TAINT) {
- if (rb_safe_level() > 0) {
- rb_raise(rb_eSecurityError, "Insecure: can't modify %"PRIsVALUE,
- RBASIC(obj)->klass);
- }
- }
}
void
@@ -2245,3 +3428,9 @@ Init_syserr(void)
#undef defined_error
#undef undefined_error
}
+
+#include "warning.rbinc"
+
+/*!
+ * \}
+ */