summaryrefslogtreecommitdiff
path: root/error.c
diff options
context:
space:
mode:
Diffstat (limited to 'error.c')
-rw-r--r--error.c2315
1 files changed, 1565 insertions, 750 deletions
diff --git a/error.c b/error.c
index 79a24a71fd..d6518feb34 100644
--- a/error.c
+++ b/error.c
@@ -23,29 +23,38 @@
# include <unistd.h>
#endif
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
#if defined __APPLE__
# include <AvailabilityMacros.h>
#endif
#include "internal.h"
+#include "internal/class.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/process.h"
+#include "internal/string.h"
#include "internal/symbol.h"
#include "internal/thread.h"
#include "internal/variable.h"
#include "ruby/encoding.h"
#include "ruby/st.h"
+#include "ruby/util.h"
#include "ruby_assert.h"
#include "vm_core.h"
+#include "yjit.h"
#include "builtin.h"
/*!
- * \defgroup exception Exception handlings
+ * \addtogroup exception
* \{
*/
@@ -76,12 +85,15 @@ static ID id_warn;
static ID id_category;
static ID id_deprecated;
static ID id_experimental;
+static ID id_performance;
+static ID id_strict_unused_block;
static VALUE sym_category;
+static VALUE sym_highlight;
static struct {
st_table *id2enum, *enum2id;
} warning_categories;
-extern const char ruby_description[];
+extern const char *rb_dynamic_description;
static const char *
rb_strerrno(int err)
@@ -98,61 +110,56 @@ static int
err_position_0(char *buf, long len, const char *file, int line)
{
if (!file) {
- return 0;
+ return 0;
}
else if (line == 0) {
- return snprintf(buf, len, "%s: ", file);
+ return snprintf(buf, len, "%s: ", file);
}
else {
- return snprintf(buf, len, "%s:%d: ", file, line);
+ return snprintf(buf, len, "%s:%d: ", file, line);
}
}
+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)
+ const char *fmt, va_list args)
{
if (file) {
- rb_str_cat2(str, file);
- if (line) rb_str_catf(str, ":%d", line);
- rb_str_cat2(str, ": ");
+ 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 VALUE syntax_error_with_path(VALUE, VALUE, VALUE*, rb_encoding*);
+
VALUE
rb_syntax_error_append(VALUE exc, VALUE file, int line, int column,
- rb_encoding *enc, const char *fmt, va_list args)
+ 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);
+ 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);
+ VALUE mesg;
+ exc = syntax_error_with_path(exc, file, &mesg, enc);
+ err_vcatf(mesg, NULL, fn, line, fmt, args);
}
return exc;
}
static unsigned int warning_disabled_categories = (
- 1U << RB_WARN_CATEGORY_DEPRECATED |
- 0);
+ (1U << RB_WARN_CATEGORY_DEPRECATED) |
+ ~RB_WARN_CATEGORY_DEFAULT_BITS);
static unsigned int
rb_warning_category_mask(VALUE category)
@@ -190,7 +197,7 @@ rb_warning_category_update(unsigned int mask, unsigned int bits)
warning_disabled_categories |= mask & ~bits;
}
-MJIT_FUNC_EXPORTED bool
+bool
rb_warning_category_enabled_p(rb_warning_category_t category)
{
return !(warning_disabled_categories & (1U << category));
@@ -203,14 +210,18 @@ rb_warning_category_enabled_p(rb_warning_category_t category)
* 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.
+ * +:deprecated+ ::
+ * deprecation warnings
+ * * assignment of non-nil value to <code>$,</code> and <code>$;</code>
+ * * keyword arguments
+ * etc.
+ *
+ * +:experimental+ ::
+ * experimental features
*
- * +:experimental+ :: experimental features
- * * Pattern matching
+ * +:performance+ ::
+ * performance hints
+ * * Shape variation limit
*/
static VALUE
@@ -243,6 +254,26 @@ rb_warning_s_aset(VALUE mod, VALUE category, VALUE flag)
/*
* call-seq:
+ * categories -> array
+ *
+ * Returns a list of the supported category symbols.
+ */
+
+static VALUE
+rb_warning_s_categories(VALUE mod)
+{
+ st_index_t num = warning_categories.id2enum->num_entries;
+ ID *ids = ALLOCA_N(ID, num);
+ num = st_keys(warning_categories.id2enum, ids, num);
+ VALUE ary = rb_ary_new_capa(num);
+ for (st_index_t i = 0; i < num; ++i) {
+ rb_ary_push(ary, ID2SYM(ids[i]));
+ }
+ return rb_ary_freeze(ary);
+}
+
+/*
+ * call-seq:
* warn(msg, category: nil) -> nil
*
* Writes warning message +msg+ to $stderr. This method is called by
@@ -282,11 +313,11 @@ rb_warning_s_warn(int argc, VALUE *argv, VALUE mod)
*
* 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.
+ * warnings somewhere other than <tt>$stderr</tt>.
*
* 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.
+ * <tt>Warning.extend(MyNewModuleWithWarnMethod)</tt> and you can use +super+
+ * to get the default behavior of printing the warning to <tt>$stderr</tt>.
*
* Example:
* module MyWarningFilter
@@ -303,7 +334,7 @@ rb_warning_s_warn(int argc, VALUE *argv, VALUE mod)
* 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.
+ * The warning[https://rubygems.org/gems/warning] gem provides convenient ways to customize Warning.warn.
*/
static VALUE
@@ -316,7 +347,8 @@ rb_warning_warn(VALUE mod, VALUE str)
static int
rb_warning_warn_arity(void)
{
- return rb_method_entry_arity(rb_method_entry(rb_singleton_class(rb_mWarning), id_warn));
+ 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
@@ -344,6 +376,7 @@ 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)
{
@@ -353,49 +386,66 @@ warn_vsprintf(rb_encoding *enc, const char *file, int line, const char *fmt, va_
return rb_str_cat2(str, "\n");
}
+#define with_warn_vsprintf(enc, file, line, fmt) \
+ VALUE str; \
+ va_list args; \
+ va_start(args, fmt); \
+ str = warn_vsprintf(enc, file, line, fmt, args); \
+ va_end(args);
+
void
rb_compile_warn(const char *file, int line, const char *fmt, ...)
{
- VALUE str;
- va_list args;
-
- if (NIL_P(ruby_verbose)) return;
+ if (!NIL_P(ruby_verbose)) {
+ with_warn_vsprintf(NULL, file, line, fmt) {
+ rb_write_warning_str(str);
+ }
+ }
+}
- va_start(args, fmt);
- str = warn_vsprintf(NULL, file, line, fmt, args);
- va_end(args);
- rb_write_warning_str(str);
+void
+rb_enc_compile_warn(rb_encoding *enc, const char *file, int line, const char *fmt, ...)
+{
+ if (!NIL_P(ruby_verbose)) {
+ with_warn_vsprintf(enc, file, line, fmt) {
+ 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;
+ if (RTEST(ruby_verbose)) {
+ with_warn_vsprintf(NULL, file, line, fmt) {
+ rb_write_warning_str(str);
+ }
+ }
+}
- va_start(args, fmt);
- str = warn_vsprintf(NULL, file, line, fmt, args);
- va_end(args);
- rb_write_warning_str(str);
+/* rb_enc_compile_warning() reports only in verbose mode */
+void
+rb_enc_compile_warning(rb_encoding *enc, const char *file, int line, const char *fmt, ...)
+{
+ if (RTEST(ruby_verbose)) {
+ with_warn_vsprintf(enc, file, line, fmt) {
+ rb_write_warning_str(str);
+ }
+ }
}
void
rb_category_compile_warn(rb_warning_category_t category, const char *file, int line, const char *fmt, ...)
{
- VALUE str;
- va_list args;
-
- if (NIL_P(ruby_verbose)) return;
-
- va_start(args, fmt);
- str = warn_vsprintf(NULL, file, line, fmt, args);
- va_end(args);
- rb_warn_category(str, rb_warning_category_to_name(category));
+ if (!NIL_P(ruby_verbose)) {
+ with_warn_vsprintf(NULL, file, line, fmt) {
+ rb_warn_category(str, rb_warning_category_to_name(category));
+ }
+ }
}
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 2, 0)
static VALUE
warning_string(rb_encoding *enc, const char *fmt, va_list args)
{
@@ -405,8 +455,10 @@ warning_string(rb_encoding *enc, const char *fmt, va_list args)
}
#define with_warning_string(mesg, enc, fmt) \
+ with_warning_string_from(mesg, enc, fmt, fmt)
+#define with_warning_string_from(mesg, enc, fmt, last_arg) \
VALUE mesg; \
- va_list args; va_start(args, fmt); \
+ va_list args; va_start(args, last_arg); \
mesg = warning_string(enc, fmt, args); \
va_end(args);
@@ -414,16 +466,16 @@ void
rb_warn(const char *fmt, ...)
{
if (!NIL_P(ruby_verbose)) {
- with_warning_string(mesg, 0, fmt) {
- rb_write_warning_str(mesg);
- }
+ with_warning_string(mesg, 0, fmt) {
+ rb_write_warning_str(mesg);
+ }
}
}
void
rb_category_warn(rb_warning_category_t category, const char *fmt, ...)
{
- if (!NIL_P(ruby_verbose)) {
+ if (!NIL_P(ruby_verbose) && rb_warning_category_enabled_p(category)) {
with_warning_string(mesg, 0, fmt) {
rb_warn_category(mesg, rb_warning_category_to_name(category));
}
@@ -434,9 +486,9 @@ 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);
- }
+ with_warning_string(mesg, enc, fmt) {
+ rb_write_warning_str(mesg);
+ }
}
}
@@ -445,9 +497,9 @@ void
rb_warning(const char *fmt, ...)
{
if (RTEST(ruby_verbose)) {
- with_warning_string(mesg, 0, fmt) {
- rb_write_warning_str(mesg);
- }
+ with_warning_string(mesg, 0, fmt) {
+ rb_write_warning_str(mesg);
+ }
}
}
@@ -455,7 +507,7 @@ rb_warning(const char *fmt, ...)
void
rb_category_warning(rb_warning_category_t category, const char *fmt, ...)
{
- if (RTEST(ruby_verbose)) {
+ if (RTEST(ruby_verbose) && rb_warning_category_enabled_p(category)) {
with_warning_string(mesg, 0, fmt) {
rb_warn_category(mesg, rb_warning_category_to_name(category));
}
@@ -475,9 +527,9 @@ 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);
- }
+ with_warning_string(mesg, enc, fmt) {
+ rb_write_warning_str(mesg);
+ }
}
}
#endif
@@ -508,12 +560,9 @@ 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);
-
- warn_deprecated(mesg, NULL, suggest);
+ with_warning_string_from(mesg, 0, fmt, suggest) {
+ warn_deprecated(mesg, NULL, suggest);
+ }
}
void
@@ -521,19 +570,28 @@ rb_warn_deprecated_to_remove(const char *removal, const char *fmt, const char *s
{
if (!deprecation_warning_enabled()) return;
- va_list args;
- va_start(args, suggest);
- VALUE mesg = warning_string(0, fmt, args);
- va_end(args);
+ with_warning_string_from(mesg, 0, fmt, suggest) {
+ warn_deprecated(mesg, removal, suggest);
+ }
+}
+
+void
+rb_warn_reserved_name(const char *coming, const char *fmt, ...)
+{
+ if (!deprecation_warning_enabled()) return;
- warn_deprecated(mesg, removal, suggest);
+ with_warning_string_from(mesg, 0, fmt, fmt) {
+ rb_str_set_len(mesg, RSTRING_LEN(mesg) - 1);
+ rb_str_catf(mesg, " is reserved for Ruby %s\n", coming);
+ rb_warn_category(mesg, ID2SYM(id_deprecated));
+ }
}
static inline int
end_with_asciichar(VALUE str, int c)
{
return RB_TYPE_P(str, T_STRING) &&
- rb_str_end_with_asciichar(str, c);
+ rb_str_end_with_asciichar(str, c);
}
/* :nodoc: */
@@ -541,7 +599,7 @@ static VALUE
warning_write(int argc, VALUE *argv, VALUE buf)
{
while (argc-- > 0) {
- rb_str_append(buf, *argv++);
+ rb_str_append(buf, *argv++);
}
return buf;
}
@@ -566,38 +624,38 @@ rb_warn_m(rb_execution_context_t *ec, VALUE exc, VALUE msgs, VALUE uplevel, VALU
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 (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 {
+ if (exc == rb_mWarning) {
+ rb_must_asciicompat(str);
+ rb_write_error_str(str);
+ }
+ else {
rb_warn_category(str, category);
- }
+ }
}
return Qnil;
}
@@ -616,7 +674,7 @@ 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 */
+ return 0; /* failed to register */
}
reporter = &bug_reporters[bug_reporters_size++];
reporter->func = func;
@@ -625,18 +683,239 @@ rb_bug_reporter_add(void (*func)(FILE *, void *), void *data)
return 1;
}
+/* returns true if x can not be used as file name */
+static bool
+path_sep_p(char x)
+{
+#if defined __CYGWIN__ || defined DOSISH
+# define PATH_SEP_ENCODING 1
+ // Assume that "/" is only the first byte in any encoding.
+ if (x == ':') return true; // drive letter or ADS
+ if (x == '\\') return true;
+#endif
+ return x == '/';
+}
+
+struct path_string {
+ const char *ptr;
+ size_t len;
+};
+
+static const char PATHSEP_REPLACE = '!';
+
+static char *
+append_pathname(char *p, const char *pe, VALUE str)
+{
+#ifdef PATH_SEP_ENCODING
+ rb_encoding *enc = rb_enc_get(str);
+#endif
+ const char *s = RSTRING_PTR(str);
+ const char *const se = s + RSTRING_LEN(str);
+ char c;
+
+ --pe; // for terminator
+
+ while (p < pe && s < se && (c = *s) != '\0') {
+ if (c == '.') {
+ if (s == se || !*s) break; // chomp "." basename
+ if (path_sep_p(s[1])) goto skipsep; // skip "./"
+ }
+ else if (path_sep_p(c)) {
+ // squeeze successive separators
+ *p++ = PATHSEP_REPLACE;
+ skipsep:
+ while (++s < se && path_sep_p(*s));
+ continue;
+ }
+ const char *const ss = s;
+ while (p < pe && s < se && *s && !path_sep_p(*s)) {
+#ifdef PATH_SEP_ENCODING
+ int n = rb_enc_mbclen(s, se, enc);
+#else
+ const int n = 1;
+#endif
+ p += n;
+ s += n;
+ }
+ if (s > ss) memcpy(p - (s - ss), ss, s - ss);
+ }
+
+ return p;
+}
+
+static char *
+append_basename(char *p, const char *pe, struct path_string *path, VALUE str)
+{
+ if (!path->ptr) {
+#ifdef PATH_SEP_ENCODING
+ rb_encoding *enc = rb_enc_get(str);
+#endif
+ const char *const b = RSTRING_PTR(str), *const e = RSTRING_END(str), *p = e;
+
+ while (p > b) {
+ if (path_sep_p(p[-1])) {
+#ifdef PATH_SEP_ENCODING
+ const char *t = rb_enc_prev_char(b, p, e, enc);
+ if (t == p-1) break;
+ p = t;
+#else
+ break;
+#endif
+ }
+ else {
+ --p;
+ }
+ }
+
+ path->ptr = p;
+ path->len = e - p;
+ }
+ size_t n = path->len;
+ if (p + n > pe) n = pe - p;
+ memcpy(p, path->ptr, n);
+ return p + n;
+}
+
+static void
+finish_report(FILE *out, rb_pid_t pid)
+{
+ if (out != stdout && out != stderr) fclose(out);
+#ifdef HAVE_WORKING_FORK
+ if (pid > 0) waitpid(pid, NULL, 0);
+#endif
+}
+
+struct report_expansion {
+ struct path_string exe, script;
+ rb_pid_t pid;
+ time_t time;
+};
+
+/*
+ * Open a bug report file to write. The `RUBY_CRASH_REPORT`
+ * environment variable can be set to define a template that is used
+ * to name bug report files. The template can contain % specifiers
+ * which are substituted by the following values when a bug report
+ * file is created:
+ *
+ * %% A single % character.
+ * %e The base name of the executable filename.
+ * %E Pathname of executable, with slashes ('/') replaced by
+ * exclamation marks ('!').
+ * %f Similar to %e with the main script filename.
+ * %F Similar to %E with the main script filename.
+ * %p PID of dumped process in decimal.
+ * %t Time of dump, expressed as seconds since the Epoch,
+ * 1970-01-01 00:00:00 +0000 (UTC).
+ * %NNN Octal char code, upto 3 digits.
+ */
+static char *
+expand_report_argument(const char **input_template, struct report_expansion *values,
+ char *buf, size_t size, bool word)
+{
+ char *p = buf;
+ char *end = buf + size;
+ const char *template = *input_template;
+ bool store = true;
+
+ if (p >= end-1 || !*template) return NULL;
+ do {
+ char c = *template++;
+ if (word && ISSPACE(c)) break;
+ if (!store) continue;
+ if (c == '%') {
+ size_t n;
+ switch (c = *template++) {
+ case 'e':
+ p = append_basename(p, end, &values->exe, rb_argv0);
+ continue;
+ case 'E':
+ p = append_pathname(p, end, rb_argv0);
+ continue;
+ case 'f':
+ p = append_basename(p, end, &values->script, GET_VM()->orig_progname);
+ continue;
+ case 'F':
+ p = append_pathname(p, end, GET_VM()->orig_progname);
+ continue;
+ case 'p':
+ if (!values->pid) values->pid = getpid();
+ snprintf(p, end-p, "%" PRI_PIDT_PREFIX "d", values->pid);
+ p += strlen(p);
+ continue;
+ case 't':
+ if (!values->time) values->time = time(NULL);
+ snprintf(p, end-p, "%" PRI_TIMET_PREFIX "d", values->time);
+ p += strlen(p);
+ continue;
+ default:
+ if (c >= '0' && c <= '7') {
+ c = (unsigned char)ruby_scan_oct(template-1, 3, &n);
+ template += n - 1;
+ if (!c) store = false;
+ }
+ break;
+ }
+ }
+ if (p < end-1) *p++ = c;
+ } while (*template);
+ *input_template = template;
+ *p = '\0';
+ return ++p;
+}
+
+FILE *ruby_popen_writer(char *const *argv, rb_pid_t *pid);
+
+static FILE *
+open_report_path(const char *template, char *buf, size_t size, rb_pid_t *pid)
+{
+ struct report_expansion values = {{0}};
+
+ if (!template) return NULL;
+ if (0) fprintf(stderr, "RUBY_CRASH_REPORT=%s\n", buf);
+ if (*template == '|') {
+ char *argv[16], *bufend = buf + size, *p;
+ int argc;
+ template++;
+ for (argc = 0; argc < numberof(argv) - 1; ++argc) {
+ while (*template && ISSPACE(*template)) template++;
+ p = expand_report_argument(&template, &values, buf, bufend-buf, true);
+ if (!p) break;
+ argv[argc] = buf;
+ buf = p;
+ }
+ argv[argc] = NULL;
+ if (!p) return ruby_popen_writer(argv, pid);
+ }
+ else if (*template) {
+ expand_report_argument(&template, &values, buf, size, false);
+ return fopen(buf, "w");
+ }
+ return NULL;
+}
+
+static const char *crash_report;
+
/* 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)
+bug_report_file(const char *file, int line, rb_pid_t *pid)
{
char buf[REPORT_BUG_BUFSIZ];
- FILE *out = stderr;
+ const char *report = crash_report;
+ if (!report) report = getenv("RUBY_CRASH_REPORT");
+ FILE *out = open_report_path(report, buf, sizeof(buf), pid);
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;
+ if (out) {
+ if ((ssize_t)fwrite(buf, 1, len, out) == (ssize_t)len) return out;
+ fclose(out);
+ }
+ if ((ssize_t)fwrite(buf, 1, len, stderr) == (ssize_t)len) {
+ return stderr;
+ }
+ if ((ssize_t)fwrite(buf, 1, len, stdout) == (ssize_t)len) {
+ return stdout;
}
return NULL;
@@ -652,40 +931,45 @@ bug_important_message(FILE *out, const char *const msg, size_t len)
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);
+ 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);
}
+#undef CRASH_REPORTER_MAY_BE_CREATED
+#if defined(__APPLE__) && \
+ (!defined(MAC_OS_X_VERSION_10_6) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6 || defined(__POWERPC__)) /* 10.6 PPC case */
+# define CRASH_REPORTER_MAY_BE_CREATED
+#endif
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"
+ "-- Crash Report log information "
+ "--------------------------------------------\n"
+ " See Crash Report log file in one of the following locations:\n"
+# ifdef CRASH_REPORTER_MAY_BE_CREATED
+ " * ~/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";
+ " * ~/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;
@@ -699,15 +983,15 @@ 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 "
+ "[IMPORTANT]"
+ /*" ------------------------------------------------"*/
+ "\n""Don't forget to include the Crash Report log file under\n"
+# ifdef CRASH_REPORTER_MAY_BE_CREATED
+ "CrashReporter or "
# endif
- "DiagnosticReports directory in bug reports.\n"
- /*"------------------------------------------------------------\n"*/
- "\n";
+ "DiagnosticReports directory in bug reports.\n"
+ /*"------------------------------------------------------------\n"*/
+ "\n";
const size_t msglen = sizeof(msg) - 1;
#else
const char *msg = NULL;
@@ -716,6 +1000,7 @@ postscript_dump(FILE *out)
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)
{
@@ -724,7 +1009,7 @@ bug_report_begin_valist(FILE *out, const char *fmt, va_list args)
fputs("[BUG] ", out);
vsnprintf(buf, sizeof(buf), fmt, args);
fputs(buf, out);
- snprintf(buf, sizeof(buf), "\n%s\n\n", ruby_description);
+ snprintf(buf, sizeof(buf), "\n%s\n\n", rb_dynamic_description);
fputs(buf, out);
preface_dump(out);
}
@@ -737,67 +1022,98 @@ bug_report_begin_valist(FILE *out, const char *fmt, va_list args)
} while (0)
static void
-bug_report_end(FILE *out)
+bug_report_end(FILE *out, rb_pid_t pid)
{
/* 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);
- }
+ 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);
+ finish_report(out, pid);
}
#define report_bug(file, line, fmt, ctx) do { \
- FILE *out = bug_report_file(file, line); \
+ rb_pid_t pid = -1; \
+ FILE *out = bug_report_file(file, line, &pid); \
if (out) { \
- bug_report_begin(out, fmt); \
- rb_vm_bugreport(ctx); \
- bug_report_end(out); \
+ bug_report_begin(out, fmt); \
+ rb_vm_bugreport(ctx, out); \
+ bug_report_end(out, pid); \
} \
} while (0) \
#define report_bug_valist(file, line, fmt, ctx, args) do { \
- FILE *out = bug_report_file(file, line); \
+ rb_pid_t pid = -1; \
+ FILE *out = bug_report_file(file, line, &pid); \
if (out) { \
- bug_report_begin_valist(out, fmt, args); \
- rb_vm_bugreport(ctx); \
- bug_report_end(out); \
+ bug_report_begin_valist(out, fmt, args); \
+ rb_vm_bugreport(ctx, out); \
+ bug_report_end(out, pid); \
} \
} while (0) \
+void
+ruby_set_crash_report(const char *template)
+{
+ crash_report = template;
+#if RUBY_DEBUG
+ rb_pid_t pid = -1;
+ char buf[REPORT_BUG_BUFSIZ];
+ FILE *out = open_report_path(template, buf, sizeof(buf), &pid);
+ if (out) {
+ time_t t = time(NULL);
+ fprintf(out, "ruby_test_bug_report: %s", ctime(&t));
+ finish_report(out, pid);
+ }
+#endif
+}
+
NORETURN(static void die(void));
static void
die(void)
{
#if defined(_WIN32) && defined(RUBY_MSVCRT_VERSION) && RUBY_MSVCRT_VERSION >= 80
+ /* mingw32 declares in stdlib.h but does not provide. */
_set_abort_behavior( 0, _CALL_REPORTFAULT);
#endif
abort();
}
-void
-rb_bug_without_die(const char *fmt, va_list args)
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 0)
+static void
+rb_bug_without_die_internal(const char *fmt, va_list args)
{
const char *file = NULL;
int line = 0;
- if (GET_EC()) {
+ if (rb_current_execution_context(false)) {
file = rb_source_location_cstr(&line);
}
report_bug_valist(file, line, fmt, NULL, args);
}
+RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 0)
+void
+rb_bug_without_die(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ rb_bug_without_die_internal(fmt, args);
+ va_end(args);
+}
+
void
rb_bug(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
- rb_bug_without_die(fmt, args);
+ rb_bug_without_die_internal(fmt, args);
va_end(args);
die();
}
@@ -808,14 +1124,15 @@ rb_bug_for_fatal_signal(ruby_sighandler_t default_sighandler, int sig, const voi
const char *file = NULL;
int line = 0;
- if (GET_EC()) {
- file = rb_source_location_cstr(&line);
+ if (rb_current_execution_context(false)) {
+ file = rb_source_location_cstr(&line);
}
report_bug(file, line, fmt, ctx);
if (default_sighandler) default_sighandler(sig);
+ ruby_default_signal(sig);
die();
}
@@ -849,17 +1166,17 @@ rb_async_bug_errno(const char *mesg, int errno_arg)
WRITE_CONST(2, "\n");
if (errno_arg == 0) {
- WRITE_CONST(2, "errno == 0 (NOERROR)\n");
+ WRITE_CONST(2, "errno == 0 (NOERROR)\n");
}
else {
- const char *errno_str = rb_strerrno(errno_arg);
+ const char *errno_str = rb_strerrno(errno_arg);
- if (!errno_str)
- errno_str = "undefined errno";
- write_or_abort(2, errno_str, strlen(errno_str));
+ if (!errno_str)
+ errno_str = "undefined errno";
+ write_or_abort(2, errno_str, strlen(errno_str));
}
WRITE_CONST(2, "\n\n");
- write_or_abort(2, ruby_description, strlen(ruby_description));
+ write_or_abort(2, rb_dynamic_description, strlen(rb_dynamic_description));
abort();
}
@@ -869,16 +1186,37 @@ rb_report_bug_valist(VALUE file, int line, const char *fmt, va_list args)
report_bug_valist(RSTRING_PTR(file), line, fmt, NULL, args);
}
-MJIT_FUNC_EXPORTED void
+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);
+ rb_assert_failure_detail(file, line, name, expr, NULL);
+}
+
+void
+rb_assert_failure_detail(const char *file, int line, const char *name, const char *expr,
+ const char *fmt, ...)
+{
+ rb_pid_t pid = -1;
+ FILE *out = bug_report_file(file, line, &pid);
+ if (out) {
+ fputs("Assertion Failed: ", out);
+ if (name) fprintf(out, "%s:", name);
+ fputs(expr, out);
+
+ if (fmt && *fmt) {
+ va_list args;
+ va_start(args, fmt);
+ fputs(": ", out);
+ vfprintf(out, fmt, args);
+ va_end(args);
+ }
+ fprintf(out, "\n%s\n\n", rb_dynamic_description);
+
+ preface_dump(out);
+ rb_vm_bugreport(NULL, out);
+ bug_report_end(out, pid);
+ }
+
die();
}
@@ -941,22 +1279,22 @@ builtin_class_name(VALUE x)
const char *etype;
if (NIL_P(x)) {
- etype = "nil";
+ etype = "nil";
}
else if (FIXNUM_P(x)) {
- etype = "Integer";
+ etype = "Integer";
}
else if (SYMBOL_P(x)) {
- etype = "Symbol";
+ etype = "Symbol";
}
else if (RB_TYPE_P(x, T_TRUE)) {
- etype = "true";
+ etype = "true";
}
else if (RB_TYPE_P(x, T_FALSE)) {
- etype = "false";
+ etype = "false";
}
else {
- etype = NULL;
+ etype = NULL;
}
return etype;
}
@@ -967,7 +1305,7 @@ rb_builtin_class_name(VALUE x)
const char *etype = builtin_class_name(x);
if (!etype) {
- etype = rb_obj_classname(x);
+ etype = rb_obj_classname(x);
}
return etype;
}
@@ -975,6 +1313,20 @@ rb_builtin_class_name(VALUE x)
COLDFUNC NORETURN(static void unexpected_type(VALUE, int, int));
#define UNDEF_LEAKED "undef leaked to the Ruby space"
+void
+rb_unexpected_typeddata(const rb_data_type_t *actual, const rb_data_type_t *expected)
+{
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)",
+ actual->wrap_struct_name, expected->wrap_struct_name);
+}
+
+void
+rb_unexpected_object_type(VALUE obj, const char *expected)
+{
+ rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected %s)",
+ displaying_class_of(obj), expected);
+}
+
static void
unexpected_type(VALUE x, int xt, int t)
{
@@ -982,16 +1334,14 @@ unexpected_type(VALUE x, int xt, int 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;
+ rb_unexpected_object_type(x, tname);
}
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);
+ 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);
+ mesg = rb_sprintf("unknown type 0x%x (0x%x given)", t, xt);
}
rb_exc_raise(rb_exc_new_str(exc, mesg));
}
@@ -1001,8 +1351,8 @@ rb_check_type(VALUE x, int t)
{
int xt;
- if (RB_UNLIKELY(x == Qundef)) {
- rb_bug(UNDEF_LEAKED);
+ if (RB_UNLIKELY(UNDEF_P(x))) {
+ rb_bug(UNDEF_LEAKED);
}
xt = TYPE(x);
@@ -1015,38 +1365,32 @@ rb_check_type(VALUE x, int t)
* 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);
+ unexpected_type(x, xt, t);
}
}
void
rb_unexpected_type(VALUE x, int t)
{
- if (RB_UNLIKELY(x == Qundef)) {
- rb_bug(UNDEF_LEAKED);
+ if (RB_UNLIKELY(UNDEF_P(x))) {
+ rb_bug(UNDEF_LEAKED);
}
unexpected_type(x, TYPE(x), t);
}
+#undef rb_typeddata_inherited_p
int
rb_typeddata_inherited_p(const rb_data_type_t *child, const rb_data_type_t *parent)
{
- while (child) {
- if (child == parent) return 1;
- child = child->parent;
- }
- return 0;
+ return rbimpl_typeddata_inherited_p_inline(child, parent);
}
+#undef rb_typeddata_is_kind_of
int
rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
{
- if (!RB_TYPE_P(obj, T_DATA) ||
- !RTYPEDDATA_P(obj) || !rb_typeddata_inherited_p(RTYPEDDATA_TYPE(obj), data_type)) {
- return 0;
- }
- return 1;
+ return rbimpl_typeddata_is_kind_of_inline(obj, data_type);
}
#undef rb_typeddata_is_instance_of
@@ -1059,26 +1403,7 @@ rb_typeddata_is_instance_of(VALUE obj, const rb_data_type_t *data_type)
void *
rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type)
{
- VALUE actual;
-
- if (!RB_TYPE_P(obj, T_DATA)) {
- actual = displaying_class_of(obj);
- }
- else if (!RTYPEDDATA_P(obj)) {
- actual = displaying_class_of(obj);
- }
- else if (!rb_typeddata_inherited_p(RTYPEDDATA_TYPE(obj), data_type)) {
- 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);
- }
-
- const char *expected = data_type->wrap_struct_name;
- rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected %s)",
- actual, expected);
- UNREACHABLE_RETURN(NULL);
+ return rbimpl_check_typeddata(obj, data_type);
}
/* exception classes */
@@ -1116,7 +1441,7 @@ 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_message, id_detailed_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;
@@ -1143,6 +1468,7 @@ rb_exc_new_cstr(VALUE etype, const char *s)
VALUE
rb_exc_new_str(VALUE etype, VALUE str)
{
+ rb_yjit_lazy_push_frame(GET_EC()->cfp->pc);
StringValue(str);
return rb_class_new_instance(1, &str, etype);
}
@@ -1157,12 +1483,23 @@ exc_init(VALUE exc, VALUE mesg)
}
/*
- * call-seq:
- * Exception.new(msg = nil) -> exception
- * Exception.exception(msg = nil) -> exception
+ * call-seq:
+ * Exception.new(message = nil) -> exception
+ *
+ * Returns a new exception object.
+ *
+ * The given +message+ should be
+ * a {string-convertible object}[rdoc-ref:implicit_conversion.rdoc@String-Convertible+Objects];
+ * see method #message;
+ * if not given, the message is the class name of the new instance
+ * (which may be the name of a subclass):
+ *
+ * Examples:
+ *
+ * Exception.new # => #<Exception: Exception>
+ * LoadError.new # => #<LoadError: LoadError> # Subclass of Exception.
+ * Exception.new('Boom') # => #<Exception: Boom>
*
- * Construct a new Exception object, optionally passing in
- * a message.
*/
static VALUE
@@ -1178,12 +1515,24 @@ exc_initialize(int argc, VALUE *argv, VALUE exc)
* Document-method: exception
*
* call-seq:
- * exc.exception([string]) -> an_exception or exc
+ * exception(message = nil) -> self or new_exception
+ *
+ * Returns an exception object of the same class as +self+;
+ * useful for creating a similar exception, but with a different message.
*
- * With no argument, or if the argument is the same as the receiver,
- * return the receiver. Otherwise, create a new
- * exception object of the same class as the receiver, but with a
- * message equal to <code>string.to_str</code>.
+ * With +message+ +nil+, returns +self+:
+ *
+ * x0 = StandardError.new('Boom') # => #<StandardError: Boom>
+ * x1 = x0.exception # => #<StandardError: Boom>
+ * x0.__id__ == x1.__id__ # => true
+ *
+ * With {string-convertible object}[rdoc-ref:implicit_conversion.rdoc@String-Convertible+Objects]
+ * +message+ (even the same as the original message),
+ * returns a new exception object whose class is the same as +self+,
+ * and whose message is the given +message+:
+ *
+ * x1 = x0.exception('Boom') # => #<StandardError: Boom>
+ * x0..equal?(x1) # => false
*
*/
@@ -1202,10 +1551,15 @@ exc_exception(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * exception.to_s -> string
+ * to_s -> string
+ *
+ * Returns a string representation of +self+:
+ *
+ * x = RuntimeError.new('Boom')
+ * x.to_s # => "Boom"
+ * x = RuntimeError.new
+ * x.to_s # => "RuntimeError"
*
- * Returns exception's message (or the name of the exception if
- * no message is set).
*/
static VALUE
@@ -1218,22 +1572,37 @@ exc_to_s(VALUE exc)
}
/* FIXME: Include eval_error.c */
-void rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE highlight, VALUE reverse);
+void rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE opt, 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 (UNDEF_P(e)) return Qnil;
+ if (!RB_TYPE_P(e, T_STRING)) e = rb_check_string_type(e);
+ return e;
+}
+
+VALUE
+rb_get_detailed_message(VALUE exc, VALUE opt)
+{
+ VALUE e;
+ if (NIL_P(opt)) {
+ e = rb_check_funcall(exc, id_detailed_message, 0, 0);
+ }
+ else {
+ e = rb_check_funcall_kw(exc, id_detailed_message, 1, &opt, 1);
+ }
+ if (UNDEF_P(e)) 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
+ * call-seq:
+ * Exception.to_tty? -> true or false
*
- * Returns +true+ if exception messages will be sent to a tty.
+ * Returns +true+ if exception messages will be sent to a terminal device.
*/
static VALUE
exc_s_to_tty_p(VALUE self)
@@ -1241,75 +1610,136 @@ exc_s_to_tty_p(VALUE self)
return RBOOL(rb_stderr_tty_p());
}
+static VALUE
+check_highlight_keyword(VALUE opt, int auto_tty_detect)
+{
+ VALUE highlight = Qnil;
+
+ if (!NIL_P(opt)) {
+ highlight = rb_hash_lookup(opt, sym_highlight);
+
+ switch (highlight) {
+ default:
+ rb_bool_expected(highlight, "highlight", TRUE);
+ UNREACHABLE;
+ case Qtrue: case Qfalse: case Qnil: break;
+ }
+ }
+
+ if (NIL_P(highlight)) {
+ highlight = RBOOL(auto_tty_detect && rb_stderr_tty_p());
+ }
+
+ return highlight;
+}
+
+static VALUE
+check_order_keyword(VALUE opt)
+{
+ VALUE order = Qnil;
+
+ if (!NIL_P(opt)) {
+ static VALUE kw_order;
+ if (!kw_order) kw_order = ID2SYM(rb_intern_const("order"));
+
+ order = rb_hash_lookup(opt, kw_order);
+
+ if (order != Qnil) {
+ ID id = rb_check_id(&order);
+ if (id == id_bottom) order = Qtrue;
+ else if (id == id_top) order = Qfalse;
+ else {
+ rb_raise(rb_eArgError, "expected :top or :bottom as "
+ "order: %+"PRIsVALUE, order);
+ }
+ }
+ }
+
+ if (NIL_P(order)) order = Qfalse;
+
+ return order;
+}
+
/*
* call-seq:
- * exception.full_message(highlight: bool, order: [:top or :bottom]) -> string
+ * full_message(highlight: true, order: :top) -> 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.
+ * Returns an enhanced message string:
*
- * If _highlight_ is +true+ the default error handler will send the
- * messages to a tty.
+ * - Includes the exception class name.
+ * - If the value of keyword +highlight+ is true (not +nil+ or +false+),
+ * includes bolding ANSI codes (see below) to enhance the appearance of the message.
+ * - Includes the {backtrace}[rdoc-ref:exceptions.md@Backtraces]:
*
- * _order_ must be either of +:top+ or +:bottom+, and places the error
- * message and the innermost backtrace come at the top or the bottom.
+ * - If the value of keyword +order+ is +:top+ (the default),
+ * lists the error message and the innermost backtrace entry first.
+ * - If the value of keyword +order+ is +:bottom+,
+ * lists the error message the innermost entry last.
*
- * The default values of these options depend on <code>$stderr</code>
- * and its +tty?+ at the timing of a call.
+ * Example:
+ *
+ * def baz
+ * begin
+ * 1 / 0
+ * rescue => x
+ * pp x.message
+ * pp x.full_message(highlight: false).split("\n")
+ * pp x.full_message.split("\n")
+ * end
+ * end
+ * def bar; baz; end
+ * def foo; bar; end
+ * foo
+ *
+ * Output:
+ *
+ * "divided by 0"
+ * ["t.rb:3:in 'Integer#/': divided by 0 (ZeroDivisionError)",
+ * "\tfrom t.rb:3:in 'Object#baz'",
+ * "\tfrom t.rb:10:in 'Object#bar'",
+ * "\tfrom t.rb:11:in 'Object#foo'",
+ * "\tfrom t.rb:12:in '<main>'"]
+ * ["t.rb:3:in 'Integer#/': \e[1mdivided by 0 (\e[1;4mZeroDivisionError\e[m\e[1m)\e[m",
+ * "\tfrom t.rb:3:in 'Object#baz'",
+ * "\tfrom t.rb:10:in 'Object#bar'",
+ * "\tfrom t.rb:11:in 'Object#foo'",
+ * "\tfrom t.rb:12:in '<main>'"]
+ *
+ * An overriding method should be careful with ANSI code enhancements;
+ * see {Messages}[rdoc-ref:exceptions.md@Messages].
*/
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};
+ VALUE highlight, order;
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]);
- }
- }
+
+ highlight = check_highlight_keyword(opt, 1);
+ order = check_order_keyword(opt);
+
+ {
+ if (NIL_P(opt)) opt = rb_hash_new();
+ rb_hash_aset(opt, sym_highlight, highlight);
}
+
str = rb_str_new2("");
errat = rb_get_backtrace(exc);
- emesg = rb_get_message(exc);
+ emesg = rb_get_detailed_message(exc, opt);
- rb_error_write(exc, emesg, errat, str, args[kw_highlight], args[kw_order]);
+ rb_error_write(exc, emesg, errat, str, opt, highlight, order);
return str;
}
/*
* call-seq:
- * exception.message -> string
+ * message -> string
*
- * Returns the result of invoking <code>exception.to_s</code>.
- * Normally this returns the exception's message or name.
+ * Returns #to_s.
+ *
+ * See {Messages}[rdoc-ref:exceptions.md@Messages].
*/
static VALUE
@@ -1320,9 +1750,74 @@ exc_message(VALUE exc)
/*
* call-seq:
- * exception.inspect -> string
+ * detailed_message(highlight: false, **kwargs) -> string
+ *
+ * Returns the message string with enhancements:
+ *
+ * - Includes the exception class name in the first line.
+ * - If the value of keyword +highlight+ is +true+,
+ * includes bolding and underlining ANSI codes (see below)
+ * to enhance the appearance of the message.
+ *
+ * Examples:
+ *
+ * begin
+ * 1 / 0
+ * rescue => x
+ * p x.message
+ * p x.detailed_message # Class name added.
+ * p x.detailed_message(highlight: true) # Class name, bolding, and underlining added.
+ * end
+ *
+ * Output:
+ *
+ * "divided by 0"
+ * "divided by 0 (ZeroDivisionError)"
+ * "\e[1mdivided by 0 (\e[1;4mZeroDivisionError\e[m\e[1m)\e[m"
+ *
+ * This method is overridden by some gems in the Ruby standard library to add information:
+ *
+ * - DidYouMean::Correctable#detailed_message.
+ * - ErrorHighlight::CoreExt#detailed_message.
+ * - SyntaxSuggest#detailed_message.
+ *
+ * An overriding method must be tolerant of passed keyword arguments,
+ * which may include (but may not be limited to):
+ *
+ * - +:highlight+.
+ * - +:did_you_mean+.
+ * - +:error_highlight+.
+ * - +:syntax_suggest+.
+ *
+ * An overriding method should also be careful with ANSI code enhancements;
+ * see {Messages}[rdoc-ref:exceptions.md@Messages].
+ */
+
+static VALUE
+exc_detailed_message(int argc, VALUE *argv, VALUE exc)
+{
+ VALUE opt;
+
+ rb_scan_args(argc, argv, "0:", &opt);
+
+ VALUE highlight = check_highlight_keyword(opt, 0);
+
+ extern VALUE rb_decorate_message(const VALUE eclass, VALUE emesg, int highlight);
+
+ return rb_decorate_message(CLASS_OF(exc), rb_get_message(exc), RTEST(highlight));
+}
+
+/*
+ * call-seq:
+ * inspect -> string
+ *
+ * Returns a string representation of +self+:
+ *
+ * x = RuntimeError.new('Boom')
+ * x.inspect # => "#<RuntimeError: Boom>"
+ * x = RuntimeError.new
+ * x.inspect # => "#<RuntimeError: RuntimeError>"
*
- * Return this exception's class name and message.
*/
static VALUE
@@ -1339,8 +1834,15 @@ exc_inspect(VALUE exc)
str = rb_str_buf_new2("#<");
klass = rb_class_name(klass);
rb_str_buf_append(str, klass);
- rb_str_buf_cat(str, ": ", 2);
- rb_str_buf_append(str, exc);
+
+ if (RTEST(rb_str_include(exc, rb_str_new2("\n")))) {
+ rb_str_catf(str, ":%+"PRIsVALUE, exc);
+ }
+ else {
+ rb_str_buf_cat(str, ": ", 2);
+ rb_str_buf_append(str, exc);
+ }
+
rb_str_buf_cat(str, ">", 1);
return str;
@@ -1348,38 +1850,36 @@ exc_inspect(VALUE exc)
/*
* call-seq:
- * exception.backtrace -> array or nil
+ * backtrace -> array or nil
*
- * Returns any backtrace associated with the exception. The backtrace
- * is an array of strings, each containing either ``filename:lineNo: in
- * `method''' or ``filename:lineNo.''
- *
- * def a
- * raise "boom"
- * end
+ * Returns the backtrace (the list of code locations that led to the exception),
+ * as an array of strings.
*
- * def b
- * a()
- * end
+ * Example (assuming the code is stored in the file named <tt>t.rb</tt>):
*
- * begin
- * b()
- * rescue => detail
- * print detail.backtrace.join("\n")
- * end
+ * def division(numerator, denominator)
+ * numerator / denominator
+ * end
*
- * <em>produces:</em>
+ * begin
+ * division(1, 0)
+ * rescue => ex
+ * p ex.backtrace
+ * # ["t.rb:2:in 'Integer#/'", "t.rb:2:in 'Object#division'", "t.rb:6:in '<main>'"]
+ * loc = ex.backtrace.first
+ * p loc.class
+ * # String
+ * end
*
- * prog.rb:2:in `a'
- * prog.rb:6:in `b'
- * prog.rb:10
+ * The value returned by this method might be adjusted when raising (see Kernel#raise),
+ * or during intermediate handling by #set_backtrace.
*
- * In the case no backtrace has been set, +nil+ is returned
+ * See also #backtrace_locations that provide the same value, as structured objects.
+ * (Note though that two values might not be consistent with each other when
+ * backtraces are manually adjusted.)
*
- * ex = StandardError.new
- * ex.backtrace
- * #=> nil
-*/
+ * see {Backtraces}[rdoc-ref:exceptions.md@Backtraces].
+ */
static VALUE
exc_backtrace(VALUE exc)
@@ -1389,8 +1889,8 @@ exc_backtrace(VALUE exc)
obj = rb_attr_get(exc, id_bt);
if (rb_backtrace_p(obj)) {
- obj = rb_backtrace_to_str_ary(obj);
- /* rb_ivar_set(exc, id_bt, obj); */
+ obj = rb_backtrace_to_str_ary(obj);
+ /* rb_ivar_set(exc, id_bt, obj); */
}
return obj;
@@ -1404,16 +1904,16 @@ 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);
+ 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);
+ info = rb_funcallv(exc, mid, 0, 0);
}
if (NIL_P(info)) return Qnil;
return rb_check_backtrace(info);
@@ -1421,13 +1921,41 @@ rb_get_backtrace(VALUE exc)
/*
* call-seq:
- * exception.backtrace_locations -> array or nil
+ * backtrace_locations -> array or nil
+ *
+ * Returns the backtrace (the list of code locations that led to the exception),
+ * as an array of Thread::Backtrace::Location instances.
+ *
+ * Example (assuming the code is stored in the file named <tt>t.rb</tt>):
+ *
+ * def division(numerator, denominator)
+ * numerator / denominator
+ * end
+ *
+ * begin
+ * division(1, 0)
+ * rescue => ex
+ * p ex.backtrace_locations
+ * # ["t.rb:2:in 'Integer#/'", "t.rb:2:in 'Object#division'", "t.rb:6:in '<main>'"]
+ * loc = ex.backtrace_locations.first
+ * p loc.class
+ * # Thread::Backtrace::Location
+ * p loc.path
+ * # "t.rb"
+ * p loc.lineno
+ * # 2
+ * p loc.label
+ * # "Integer#/"
+ * end
+ *
+ * The value returned by this method might be adjusted when raising (see Kernel#raise),
+ * or during intermediate handling by #set_backtrace.
*
- * Returns any backtrace associated with the exception. This method is
- * similar to Exception#backtrace, but the backtrace is an array of
- * Thread::Backtrace::Location.
+ * See also #backtrace that provide the same value as an array of strings.
+ * (Note though that two values might not be consistent with each other when
+ * backtraces are manually adjusted.)
*
- * This method is not affected by Exception#set_backtrace().
+ * See {Backtraces}[rdoc-ref:exceptions.md@Backtraces].
*/
static VALUE
exc_backtrace_locations(VALUE exc)
@@ -1436,7 +1964,7 @@ exc_backtrace_locations(VALUE exc)
obj = rb_attr_get(exc, id_bt_locations);
if (!NIL_P(obj)) {
- obj = rb_backtrace_to_location_ary(obj);
+ obj = rb_backtrace_to_location_ary(obj);
}
return obj;
}
@@ -1445,53 +1973,176 @@ static VALUE
rb_check_backtrace(VALUE bt)
{
long i;
- static const char err[] = "backtrace must be Array of String";
+ static const char err[] = "backtrace must be an Array of String or an Array of Thread::Backtrace::Location";
if (!NIL_P(bt)) {
- if (RB_TYPE_P(bt, T_STRING)) return rb_ary_new3(1, bt);
- if (rb_backtrace_p(bt)) return bt;
- if (!RB_TYPE_P(bt, T_ARRAY)) {
- rb_raise(rb_eTypeError, err);
- }
- for (i=0;i<RARRAY_LEN(bt);i++) {
- VALUE e = RARRAY_AREF(bt, i);
- if (!RB_TYPE_P(e, T_STRING)) {
- rb_raise(rb_eTypeError, err);
- }
- }
+ if (RB_TYPE_P(bt, T_STRING)) return rb_ary_new3(1, bt);
+ if (rb_backtrace_p(bt)) return bt;
+ if (!RB_TYPE_P(bt, T_ARRAY)) {
+ rb_raise(rb_eTypeError, err);
+ }
+ for (i=0;i<RARRAY_LEN(bt);i++) {
+ VALUE e = RARRAY_AREF(bt, i);
+ if (!RB_TYPE_P(e, T_STRING)) {
+ rb_raise(rb_eTypeError, err);
+ }
+ }
}
return bt;
}
/*
* call-seq:
- * exc.set_backtrace(backtrace) -> array
+ * set_backtrace(value) -> value
+ *
+ * Sets the backtrace value for +self+; returns the given +value+.
+ *
+ * The +value+ might be:
+ *
+ * - an array of Thread::Backtrace::Location;
+ * - an array of String instances;
+ * - a single String instance; or
+ * - +nil+.
*
- * Sets the backtrace information associated with +exc+. The +backtrace+ must
- * be an array of String objects or a single String in the format described
- * in Exception#backtrace.
+ * Using array of Thread::Backtrace::Location is the most consistent
+ * option: it sets both #backtrace and #backtrace_locations. It should be
+ * preferred when possible. The suitable array of locations can be obtained
+ * from Kernel#caller_locations, copied from another error, or just set to
+ * the adjusted result of the current error's #backtrace_locations:
+ *
+ * require 'json'
+ *
+ * def parse_payload(text)
+ * JSON.parse(text) # test.rb, line 4
+ * rescue JSON::ParserError => ex
+ * ex.set_backtrace(ex.backtrace_locations[2...])
+ * raise
+ * end
+ *
+ * parse_payload('{"wrong: "json"')
+ * # test.rb:4:in 'Object#parse_payload': unexpected token at '{"wrong: "json"' (JSON::ParserError)
+ * #
+ * # An error points to the body of parse_payload method,
+ * # hiding the parts of the backtrace related to the internals
+ * # of the "json" library
+ *
+ * # The error has both #backtace and #backtrace_locations set
+ * # consistently:
+ * begin
+ * parse_payload('{"wrong: "json"')
+ * rescue => ex
+ * p ex.backtrace
+ * # ["test.rb:4:in 'Object#parse_payload'", "test.rb:20:in '<main>'"]
+ * p ex.backtrace_locations
+ * # ["test.rb:4:in 'Object#parse_payload'", "test.rb:20:in '<main>'"]
+ * end
+ *
+ * When the desired stack of locations is not available and should
+ * be constructed from scratch, an array of strings or a singular
+ * string can be used. In this case, only #backtrace is affected:
+ *
+ * def parse_payload(text)
+ * JSON.parse(text)
+ * rescue JSON::ParserError => ex
+ * ex.set_backtrace(["dsl.rb:34", "framework.rb:1"])
+ * # The error have the new value in #backtrace:
+ * p ex.backtrace
+ * # ["dsl.rb:34", "framework.rb:1"]
+ *
+ * # but the original one in #backtrace_locations
+ * p ex.backtrace_locations
+ * # [".../json/common.rb:221:in 'JSON::Ext::Parser.parse'", ...]
+ * end
+ *
+ * parse_payload('{"wrong: "json"')
+ *
+ * Calling #set_backtrace with +nil+ clears up #backtrace but doesn't affect
+ * #backtrace_locations:
+ *
+ * def parse_payload(text)
+ * JSON.parse(text)
+ * rescue JSON::ParserError => ex
+ * ex.set_backtrace(nil)
+ * p ex.backtrace
+ * # nil
+ * p ex.backtrace_locations
+ * # [".../json/common.rb:221:in 'JSON::Ext::Parser.parse'", ...]
+ * end
+ *
+ * parse_payload('{"wrong: "json"')
+ *
+ * On reraising of such an exception, both #backtrace and #backtrace_locations
+ * is set to the place of reraising:
+ *
+ * def parse_payload(text)
+ * JSON.parse(text)
+ * rescue JSON::ParserError => ex
+ * ex.set_backtrace(nil)
+ * raise # test.rb, line 7
+ * end
*
+ * begin
+ * parse_payload('{"wrong: "json"')
+ * rescue => ex
+ * p ex.backtrace
+ * # ["test.rb:7:in 'Object#parse_payload'", "test.rb:11:in '<main>'"]
+ * p ex.backtrace_locations
+ * # ["test.rb:7:in 'Object#parse_payload'", "test.rb:11:in '<main>'"]
+ * end
+ *
+ * See {Backtraces}[rdoc-ref:exceptions.md@Backtraces].
*/
static VALUE
exc_set_backtrace(VALUE exc, VALUE bt)
{
- return rb_ivar_set(exc, id_bt, rb_check_backtrace(bt));
+ VALUE btobj = rb_location_ary_to_backtrace(bt);
+ if (RTEST(btobj)) {
+ rb_ivar_set(exc, id_bt, btobj);
+ rb_ivar_set(exc, id_bt_locations, btobj);
+ return bt;
+ }
+ else {
+ return rb_ivar_set(exc, id_bt, rb_check_backtrace(bt));
+ }
}
-MJIT_FUNC_EXPORTED VALUE
+VALUE
rb_exc_set_backtrace(VALUE exc, VALUE bt)
{
return exc_set_backtrace(exc, bt);
}
/*
- * call-seq:
- * exception.cause -> an_exception or nil
+ * call-seq:
+ * cause -> exception or nil
+ *
+ * Returns the previous value of global variable <tt>$!</tt>,
+ * which may be +nil+
+ * (see {Global Variables}[rdoc-ref:exceptions.md@Global+Variables]):
+ *
+ * begin
+ * raise('Boom 0')
+ * rescue => x0
+ * puts "Exception: #{x0}; $!: #{$!}; cause: #{x0.cause.inspect}."
+ * begin
+ * raise('Boom 1')
+ * rescue => x1
+ * puts "Exception: #{x1}; $!: #{$!}; cause: #{x1.cause}."
+ * begin
+ * raise('Boom 2')
+ * rescue => x2
+ * puts "Exception: #{x2}; $!: #{$!}; cause: #{x2.cause}."
+ * end
+ * end
+ * end
+ *
+ * Output:
+ *
+ * Exception: Boom 0; $!: Boom 0; cause: nil.
+ * Exception: Boom 1; $!: Boom 1; cause: Boom 0.
+ * Exception: Boom 2; $!: Boom 2; cause: Boom 1.
*
- * 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
@@ -1508,11 +2159,11 @@ try_convert_to_exception(VALUE obj)
/*
* call-seq:
- * exc == obj -> true or false
+ * self == other -> true or false
+ *
+ * Returns whether +other+ is the same class as +self+
+ * and its #message and #backtrace are equal to those of +self+.
*
- * 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.
*/
static VALUE
@@ -1523,29 +2174,27 @@ exc_equal(VALUE exc, VALUE obj)
if (exc == obj) return Qtrue;
if (rb_obj_class(exc) != rb_obj_class(obj)) {
- int state;
-
- obj = rb_protect(try_convert_to_exception, obj, &state);
- if (state || obj == Qundef) {
- rb_set_errinfo(Qnil);
- return Qfalse;
- }
- if (rb_obj_class(exc) != rb_obj_class(obj)) return Qfalse;
- mesg = rb_check_funcall(obj, id_message, 0, 0);
- if (mesg == Qundef) return Qfalse;
- backtrace = rb_check_funcall(obj, id_backtrace, 0, 0);
- if (backtrace == Qundef) return Qfalse;
+ int state;
+
+ obj = rb_protect(try_convert_to_exception, obj, &state);
+ if (state || UNDEF_P(obj)) {
+ rb_set_errinfo(Qnil);
+ return Qfalse;
+ }
+ if (rb_obj_class(exc) != rb_obj_class(obj)) return Qfalse;
+ mesg = rb_check_funcall(obj, id_message, 0, 0);
+ if (UNDEF_P(mesg)) return Qfalse;
+ backtrace = rb_check_funcall(obj, id_backtrace, 0, 0);
+ if (UNDEF_P(backtrace)) return Qfalse;
}
else {
- mesg = rb_attr_get(obj, id_mesg);
- backtrace = exc_backtrace(obj);
+ mesg = rb_attr_get(obj, id_mesg);
+ backtrace = exc_backtrace(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 Qfalse;
+ return rb_equal(exc_backtrace(exc), backtrace);
}
/*
@@ -1565,37 +2214,37 @@ exit_initialize(int argc, VALUE *argv, VALUE exc)
{
VALUE status;
if (argc > 0) {
- status = *argv;
-
- switch (status) {
- case Qtrue:
- status = INT2FIX(EXIT_SUCCESS);
- ++argv;
- --argc;
- break;
- case Qfalse:
- status = INT2FIX(EXIT_FAILURE);
- ++argv;
- --argc;
- break;
- default:
- status = rb_check_to_int(status);
- if (NIL_P(status)) {
- status = INT2FIX(EXIT_SUCCESS);
- }
- else {
+ status = *argv;
+
+ switch (status) {
+ case Qtrue:
+ status = INT2FIX(EXIT_SUCCESS);
+ ++argv;
+ --argc;
+ break;
+ case Qfalse:
+ status = INT2FIX(EXIT_FAILURE);
+ ++argv;
+ --argc;
+ break;
+ default:
+ status = rb_check_to_int(status);
+ if (NIL_P(status)) {
+ status = INT2FIX(EXIT_SUCCESS);
+ }
+ else {
#if EXIT_SUCCESS != 0
- if (status == INT2FIX(0))
- status = INT2FIX(EXIT_SUCCESS);
+ if (status == INT2FIX(0))
+ status = INT2FIX(EXIT_SUCCESS);
#endif
- ++argv;
- --argc;
- }
- break;
- }
+ ++argv;
+ --argc;
+ }
+ break;
+ }
}
else {
- status = INT2FIX(EXIT_SUCCESS);
+ status = INT2FIX(EXIT_SUCCESS);
}
rb_call_super(argc, argv);
rb_ivar_set(exc, id_status, status);
@@ -1631,18 +2280,15 @@ exit_success_p(VALUE exc)
int status;
if (NIL_P(status_val))
- return Qtrue;
+ return Qtrue;
status = NUM2INT(status_val);
- if (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS)
- return Qtrue;
-
- return Qfalse;
+ return RBOOL(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS);
}
static VALUE
err_init_recv(VALUE exc, VALUE recv)
{
- if (recv != Qundef) rb_ivar_set(exc, id_recv, recv);
+ if (!UNDEF_P(recv)) rb_ivar_set(exc, id_recv, recv);
return exc;
}
@@ -1720,7 +2366,9 @@ name_err_init_attr(VALUE exc, VALUE recv, VALUE method)
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));
+ if (cfp && VM_FRAME_TYPE(cfp) != VM_FRAME_MAGIC_DUMMY) {
+ rb_ivar_set(exc, id_iseq, rb_iseqw_new(cfp->iseq));
+ }
return exc;
}
@@ -1797,10 +2445,10 @@ 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);
+ 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;
}
@@ -1850,50 +2498,41 @@ rb_nomethod_err_new(VALUE mesg, VALUE recv, VALUE method, VALUE args, int priv)
return nometh_err_init_attr(exc, args, priv);
}
-/* :nodoc: */
-enum {
- NAME_ERR_MESG__MESG,
- NAME_ERR_MESG__RECV,
- NAME_ERR_MESG__NAME,
- NAME_ERR_MESG_COUNT
-};
+typedef struct name_error_message_struct {
+ VALUE mesg;
+ VALUE recv;
+ VALUE name;
+} name_error_message_t;
static void
-name_err_mesg_mark(void *p)
+name_err_mesg_mark_and_move(void *p)
{
- VALUE *ptr = p;
- rb_gc_mark_locations(ptr, ptr+NAME_ERR_MESG_COUNT);
-}
-
-#define name_err_mesg_free RUBY_TYPED_DEFAULT_FREE
-
-static size_t
-name_err_mesg_memsize(const void *p)
-{
- return NAME_ERR_MESG_COUNT * sizeof(VALUE);
+ name_error_message_t *ptr = (name_error_message_t *)p;
+ rb_gc_mark_and_move(&ptr->mesg);
+ rb_gc_mark_and_move(&ptr->recv);
+ rb_gc_mark_and_move(&ptr->name);
}
static const rb_data_type_t name_err_mesg_data_type = {
"name_err_mesg",
{
- name_err_mesg_mark,
- name_err_mesg_free,
- name_err_mesg_memsize,
+ name_err_mesg_mark_and_move,
+ RUBY_TYPED_DEFAULT_FREE,
+ NULL, // No external memory to report,
+ name_err_mesg_mark_and_move,
},
- 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};
/* :nodoc: */
static VALUE
-rb_name_err_mesg_init(VALUE klass, VALUE mesg, VALUE recv, VALUE method)
+rb_name_err_mesg_init(VALUE klass, VALUE mesg, VALUE recv, VALUE name)
{
- VALUE result = TypedData_Wrap_Struct(klass, &name_err_mesg_data_type, 0);
- VALUE *ptr = ALLOC_N(VALUE, NAME_ERR_MESG_COUNT);
-
- ptr[NAME_ERR_MESG__MESG] = mesg;
- ptr[NAME_ERR_MESG__RECV] = recv;
- ptr[NAME_ERR_MESG__NAME] = method;
- RTYPEDDATA_DATA(result) = ptr;
+ name_error_message_t *message;
+ VALUE result = TypedData_Make_Struct(klass, name_error_message_t, &name_err_mesg_data_type, message);
+ RB_OBJ_WRITE(result, &message->mesg, mesg);
+ RB_OBJ_WRITE(result, &message->recv, recv);
+ RB_OBJ_WRITE(result, &message->name, name);
return result;
}
@@ -1915,14 +2554,16 @@ name_err_mesg_alloc(VALUE klass)
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);
+ name_error_message_t *ptr1, *ptr2;
+ TypedData_Get_Struct(obj1, name_error_message_t, &name_err_mesg_data_type, ptr1);
+ TypedData_Get_Struct(obj2, name_error_message_t, &name_err_mesg_data_type, ptr2);
+
+ RB_OBJ_WRITE(obj1, &ptr1->mesg, ptr2->mesg);
+ RB_OBJ_WRITE(obj1, &ptr1->recv, ptr2->recv);
+ RB_OBJ_WRITE(obj1, &ptr1->name, ptr2->name);
return obj1;
}
@@ -1930,19 +2571,18 @@ name_err_mesg_init_copy(VALUE obj1, VALUE obj2)
static VALUE
name_err_mesg_equal(VALUE obj1, VALUE obj2)
{
- VALUE *ptr1, *ptr2;
- int i;
-
if (obj1 == obj2) return Qtrue;
+
if (rb_obj_class(obj2) != rb_cNameErrorMesg)
- return Qfalse;
+ return Qfalse;
- TypedData_Get_Struct(obj1, VALUE, &name_err_mesg_data_type, ptr1);
- TypedData_Get_Struct(obj2, VALUE, &name_err_mesg_data_type, ptr2);
- for (i=0; i<NAME_ERR_MESG_COUNT; i++) {
- if (!rb_equal(ptr1[i], ptr2[i]))
- return Qfalse;
- }
+ name_error_message_t *ptr1, *ptr2;
+ TypedData_Get_Struct(obj1, name_error_message_t, &name_err_mesg_data_type, ptr1);
+ TypedData_Get_Struct(obj2, name_error_message_t, &name_err_mesg_data_type, ptr2);
+
+ if (!rb_equal(ptr1->mesg, ptr2->mesg)) return Qfalse;
+ if (!rb_equal(ptr1->recv, ptr2->recv)) return Qfalse;
+ if (!rb_equal(ptr1->name, ptr2->name)) return Qfalse;
return Qtrue;
}
@@ -1961,55 +2601,95 @@ name_err_mesg_receiver_name(VALUE obj)
static VALUE
name_err_mesg_to_str(VALUE obj)
{
- VALUE *ptr, mesg;
- TypedData_Get_Struct(obj, VALUE, &name_err_mesg_data_type, ptr);
+ name_error_message_t *ptr;
+ TypedData_Get_Struct(obj, name_error_message_t, &name_err_mesg_data_type, ptr);
- mesg = ptr[NAME_ERR_MESG__MESG];
+ VALUE mesg = ptr->mesg;
if (NIL_P(mesg)) return Qnil;
else {
- struct RString s_str, d_str;
- VALUE c, s, d = 0, args[4];
- int state = 0, singleton = 0;
- rb_encoding *usascii = rb_usascii_encoding();
+ struct RString s_str = {RBASIC_INIT}, c_str = {RBASIC_INIT}, d_str = {RBASIC_INIT};
+ VALUE c, s, d = 0, args[4], c2;
+ int state = 0;
+ rb_encoding *usascii = rb_usascii_encoding();
#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:
- d = FAKE_CSTR(&d_str, "nil");
- break;
- case Qtrue:
- d = FAKE_CSTR(&d_str, "true");
- break;
- case Qfalse:
- d = FAKE_CSTR(&d_str, "false");
- break;
- default:
- d = rb_protect(name_err_mesg_receiver_name, obj, &state);
- if (state || d == Qundef || d == Qnil)
- d = rb_protect(rb_inspect, obj, &state);
- if (state) {
- rb_set_errinfo(Qnil);
- }
- d = rb_check_string_type(d);
- if (NIL_P(d)) {
- d = rb_any_to_s(obj);
- }
- singleton = (RSTRING_LEN(d) > 0 && RSTRING_PTR(d)[0] == '#');
- break;
- }
- if (!singleton) {
- s = FAKE_CSTR(&s_str, ":");
- c = rb_class_name(CLASS_OF(obj));
- }
- 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);
+ c = s = FAKE_CSTR(&s_str, "");
+ obj = ptr->recv;
+ switch (obj) {
+ case Qnil:
+ c = d = FAKE_CSTR(&d_str, "nil");
+ break;
+ case Qtrue:
+ c = d = FAKE_CSTR(&d_str, "true");
+ break;
+ case Qfalse:
+ c = d = FAKE_CSTR(&d_str, "false");
+ break;
+ default:
+ if (strstr(RSTRING_PTR(mesg), "%2$s")) {
+ d = rb_protect(name_err_mesg_receiver_name, obj, &state);
+ if (state || NIL_OR_UNDEF_P(d))
+ d = rb_protect(rb_inspect, obj, &state);
+ if (state) {
+ rb_set_errinfo(Qnil);
+ }
+ d = rb_check_string_type(d);
+ if (NIL_P(d)) {
+ d = rb_any_to_s(obj);
+ }
+ }
+
+ if (!RB_SPECIAL_CONST_P(obj)) {
+ switch (RB_BUILTIN_TYPE(obj)) {
+ case T_MODULE:
+ s = FAKE_CSTR(&s_str, "module ");
+ c = obj;
+ break;
+ case T_CLASS:
+ s = FAKE_CSTR(&s_str, "class ");
+ c = obj;
+ break;
+ default:
+ goto object;
+ }
+ }
+ else {
+ VALUE klass;
+ object:
+ klass = CLASS_OF(obj);
+ if (RB_TYPE_P(klass, T_CLASS) && RCLASS_SINGLETON_P(klass)) {
+ s = FAKE_CSTR(&s_str, "");
+ if (obj == rb_vm_top_self()) {
+ c = FAKE_CSTR(&c_str, "main");
+ }
+ else {
+ c = rb_any_to_s(obj);
+ }
+ break;
+ }
+ else {
+ s = FAKE_CSTR(&s_str, "an instance of ");
+ c = rb_class_real(klass);
+ }
+ }
+ c2 = rb_protect(name_err_mesg_receiver_name, c, &state);
+ if (state || NIL_OR_UNDEF_P(c2))
+ c2 = rb_protect(rb_inspect, c, &state);
+ if (state) {
+ rb_set_errinfo(Qnil);
+ }
+ c2 = rb_check_string_type(c2);
+ if (NIL_P(c2)) {
+ c2 = rb_any_to_s(c);
+ }
+ c = c2;
+ break;
+ }
+ args[0] = rb_obj_as_string(ptr->name);
+ args[1] = d;
+ args[2] = s;
+ args[3] = c;
+ mesg = rb_str_format(4, args, mesg);
}
return mesg;
}
@@ -2038,17 +2718,17 @@ name_err_mesg_load(VALUE klass, VALUE str)
static VALUE
name_err_receiver(VALUE self)
{
- VALUE *ptr, recv, mesg;
-
- recv = rb_ivar_lookup(self, id_recv, Qundef);
- if (recv != Qundef) return recv;
+ VALUE recv = rb_ivar_lookup(self, id_recv, Qundef);
+ if (!UNDEF_P(recv)) return recv;
- mesg = rb_attr_get(self, id_mesg);
+ VALUE 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");
+ rb_raise(rb_eArgError, "no receiver is available");
}
- ptr = DATA_PTR(mesg);
- return ptr[NAME_ERR_MESG__RECV];
+
+ name_error_message_t *ptr;
+ TypedData_Get_Struct(mesg, name_error_message_t, &name_err_mesg_data_type, ptr);
+ return ptr->recv;
}
/*
@@ -2099,7 +2779,7 @@ key_err_receiver(VALUE self)
VALUE recv;
recv = rb_ivar_lookup(self, id_receiver, Qundef);
- if (recv != Qundef) return recv;
+ if (!UNDEF_P(recv)) return recv;
rb_raise(rb_eArgError, "no receiver is available");
}
@@ -2116,7 +2796,7 @@ key_err_key(VALUE self)
VALUE key;
key = rb_ivar_lookup(self, id_key, Qundef);
- if (key != Qundef) return key;
+ if (!UNDEF_P(key)) return key;
rb_raise(rb_eArgError, "no key is available");
}
@@ -2147,17 +2827,17 @@ key_err_initialize(int argc, VALUE *argv, VALUE self)
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]);
- }
- }
+ 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 (!UNDEF_P(values[i])) {
+ rb_ivar_set(self, keywords[i], values[i]);
+ }
+ }
}
return self;
@@ -2176,7 +2856,7 @@ no_matching_pattern_key_err_matchee(VALUE self)
VALUE matchee;
matchee = rb_ivar_lookup(self, id_matchee, Qundef);
- if (matchee != Qundef) return matchee;
+ if (!UNDEF_P(matchee)) return matchee;
rb_raise(rb_eArgError, "no matchee is available");
}
@@ -2193,7 +2873,7 @@ no_matching_pattern_key_err_key(VALUE self)
VALUE key;
key = rb_ivar_lookup(self, id_key, Qundef);
- if (key != Qundef) return key;
+ if (!UNDEF_P(key)) return key;
rb_raise(rb_eArgError, "no key is available");
}
@@ -2220,7 +2900,7 @@ no_matching_pattern_key_err_initialize(int argc, VALUE *argv, VALUE self)
keywords[1] = id_key;
rb_get_kwargs(options, keywords, 0, numberof(values), values);
for (i = 0; i < numberof(values); ++i) {
- if (values[i] != Qundef) {
+ if (!UNDEF_P(values[i])) {
rb_ivar_set(self, keywords[i], values[i]);
}
}
@@ -2242,77 +2922,144 @@ syntax_error_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE mesg;
if (argc == 0) {
- mesg = rb_fstring_lit("compile error");
- argc = 1;
- argv = &mesg;
+ mesg = rb_fstring_lit("compile error");
+ argc = 1;
+ argv = &mesg;
}
return rb_call_super(argc, argv);
}
+static VALUE
+syntax_error_with_path(VALUE exc, VALUE path, VALUE *mesg, rb_encoding *enc)
+{
+ if (NIL_P(exc)) {
+ *mesg = rb_enc_str_new(0, 0, enc);
+ exc = rb_class_new_instance(1, mesg, rb_eSyntaxError);
+ rb_ivar_set(exc, id_i_path, path);
+ }
+ else {
+ VALUE old_path = rb_attr_get(exc, id_i_path);
+ if (old_path != path) {
+ if (rb_str_equal(path, old_path)) {
+ rb_raise(rb_eArgError, "SyntaxError#path changed: %+"PRIsVALUE" (%p->%p)",
+ old_path, (void *)old_path, (void *)path);
+ }
+ else {
+ rb_raise(rb_eArgError, "SyntaxError#path changed: %+"PRIsVALUE"(%s%s)->%+"PRIsVALUE"(%s)",
+ old_path, rb_enc_name(rb_enc_get(old_path)),
+ (FL_TEST(old_path, RSTRING_FSTR) ? ":FSTR" : ""),
+ path, rb_enc_name(rb_enc_get(path)));
+ }
+ }
+ VALUE s = *mesg = rb_attr_get(exc, idMesg);
+ if (RSTRING_LEN(s) > 0 && *(RSTRING_END(s)-1) != '\n')
+ rb_str_cat_cstr(s, "\n");
+ }
+ return exc;
+}
+
/*
* Document-module: Errno
*
- * 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>.
+ * When an operating system encounters an error,
+ * it typically reports the error as an integer error code:
+ *
+ * $ ls nosuch.txt
+ * ls: cannot access 'nosuch.txt': No such file or directory
+ * $ echo $? # Code for last error.
+ * 2
+ *
+ * When the Ruby interpreter interacts with the operating system
+ * and receives such an error code (e.g., +2+),
+ * it maps the code to a particular Ruby exception class (e.g., +Errno::ENOENT+):
+ *
+ * File.open('nosuch.txt')
+ * # => No such file or directory @ rb_sysopen - nosuch.txt (Errno::ENOENT)
+ *
+ * Each such class is:
*
- * 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.
+ * - A nested class in this module, +Errno+.
+ * - A subclass of class SystemCallError.
+ * - Associated with an error code.
*
- * The integer operating system error number corresponding to a
- * particular error is available as the class constant
- * <code>Errno::</code><em>error</em><code>::Errno</code>.
+ * Thus:
*
- * Errno::EACCES::Errno #=> 13
- * Errno::EAGAIN::Errno #=> 11
- * Errno::EINTR::Errno #=> 4
+ * Errno::ENOENT.superclass # => SystemCallError
+ * Errno::ENOENT::Errno # => 2
*
- * The full list of operating system errors on your particular platform
- * are available as the constants of Errno.
+ * The names of nested classes are returned by method +Errno.constants+:
*
- * Errno.constants #=> :E2BIG, :EACCES, :EADDRINUSE, :EADDRNOTAVAIL, ...
+ * Errno.constants.size # => 158
+ * Errno.constants.sort.take(5) # => [:E2BIG, :EACCES, :EADDRINUSE, :EADDRNOTAVAIL, :EADV]
+ *
+ * As seen above, the error code associated with each class
+ * is available as the value of a constant;
+ * the value for a particular class may vary among operating systems.
+ * If the class is not needed for the particular operating system,
+ * the value is zero:
+ *
+ * Errno::ENOENT::Errno # => 2
+ * Errno::ENOTCAPABLE::Errno # => 0
+ *
+ * Each class in Errno can be created with optional messages:
+ *
+ * Errno::EPIPE.new # => #<Errno::EPIPE: Broken pipe>
+ * Errno::EPIPE.new("foo") # => #<Errno::EPIPE: Broken pipe - foo>
+ * Errno::EPIPE.new("foo", "here") # => #<Errno::EPIPE: Broken pipe @ here - foo>
+ *
+ * See SystemCallError.new.
*/
static st_table *syserr_tbl;
-static VALUE
-set_syserr(int n, const char *name)
+void
+rb_free_warning(void)
{
- st_data_t error;
+ st_free_table(warning_categories.id2enum);
+ st_free_table(warning_categories.enum2id);
+ st_free_table(syserr_tbl);
+}
- if (!st_lookup(syserr_tbl, n, &error)) {
- error = rb_define_class_under(rb_mErrno, name, rb_eSystemCallError);
+static VALUE
+setup_syserr(int n, const char *name)
+{
+ VALUE error = rb_define_class_under(rb_mErrno, name, rb_eSystemCallError);
- /* capture nonblock errnos for WaitReadable/WaitWritable subclasses */
- switch (n) {
- case EAGAIN:
- rb_eEAGAIN = error;
+ /* capture nonblock errnos for WaitReadable/WaitWritable subclasses */
+ switch (n) {
+ case EAGAIN:
+ rb_eEAGAIN = error;
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
- break;
- case EWOULDBLOCK:
+ break;
+ case EWOULDBLOCK:
#endif
- rb_eEWOULDBLOCK = error;
- break;
- case EINPROGRESS:
- rb_eEINPROGRESS = error;
- break;
- }
+ rb_eEWOULDBLOCK = error;
+ break;
+ case EINPROGRESS:
+ rb_eEINPROGRESS = error;
+ break;
+ }
- rb_define_const(error, "Errno", INT2NUM(n));
- st_add_direct(syserr_tbl, n, error);
+ rb_define_const(error, "Errno", INT2NUM(n));
+ st_add_direct(syserr_tbl, n, (st_data_t)error);
+ return error;
+}
+
+static VALUE
+set_syserr(int n, const char *name)
+{
+ st_data_t error;
+
+ if (!st_lookup(syserr_tbl, n, &error)) {
+ return setup_syserr(n, name);
}
else {
- rb_define_const(rb_mErrno, name, error);
+ VALUE errclass = (VALUE)error;
+ rb_define_const(rb_mErrno, name, errclass);
+ return errclass;
}
- return error;
}
static VALUE
@@ -2321,62 +3068,80 @@ get_syserr(int n)
st_data_t error;
if (!st_lookup(syserr_tbl, n, &error)) {
- char name[8]; /* some Windows' errno have 5 digits. */
+ char name[DECIMAL_SIZE_OF(n) + sizeof("E-")];
- snprintf(name, sizeof(name), "E%03d", n);
- error = set_syserr(n, name);
+ snprintf(name, sizeof(name), "E%03d", n);
+ return setup_syserr(n, name);
}
- return error;
+ return (VALUE)error;
}
/*
* call-seq:
- * SystemCallError.new(msg, errno) -> system_call_error_subclass
+ * SystemCallError.new(msg, errno = nil, func = nil) -> system_call_error_subclass
*
* 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.
+ *
+ * If only numeric object is given, it is treated as an Integer _errno_,
+ * and _msg_ is omitted, otherwise the first argument _msg_ is used as
+ * the additional error message.
+ *
+ * SystemCallError.new(Errno::EPIPE::Errno)
+ * #=> #<Errno::EPIPE: Broken pipe>
+ *
+ * SystemCallError.new("foo")
+ * #=> #<SystemCallError: unknown error - foo>
+ *
+ * SystemCallError.new("foo", Errno::EPIPE::Errno)
+ * #=> #<Errno::EPIPE: Broken pipe - foo>
+ *
+ * If _func_ is not +nil+, it is appended to the message with "<tt> @ </tt>".
+ *
+ * SystemCallError.new("foo", Errno::EPIPE::Errno, "here")
+ * #=> #<Errno::EPIPE: Broken pipe @ here - foo>
+ *
+ * A subclass of SystemCallError can also be instantiated via the
+ * +new+ method of the subclass. See Errno.
*/
static VALUE
syserr_initialize(int argc, VALUE *argv, VALUE self)
{
-#if !defined(_WIN32)
- char *strerror();
-#endif
const char *err;
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, "12", &mesg, &error, &func);
- if (argc == 1 && FIXNUM_P(mesg)) {
- error = mesg; mesg = Qnil;
- }
- if (!NIL_P(error) && st_lookup(syserr_tbl, NUM2LONG(error), &data)) {
- klass = (VALUE)data;
- /* change class */
- if (!RB_TYPE_P(self, T_OBJECT)) { /* insurance to avoid type crash */
- rb_raise(rb_eTypeError, "invalid instance type");
- }
- RBASIC_SET_CLASS(self, klass);
- }
+ st_data_t data = (st_data_t)klass;
+ rb_scan_args(argc, argv, "12", &mesg, &error, &func);
+ if (argc == 1 && FIXNUM_P(mesg)) {
+ error = mesg; mesg = Qnil;
+ }
+ if (!NIL_P(error) && st_lookup(syserr_tbl, NUM2LONG(error), &data)) {
+ klass = (VALUE)data;
+ /* change class */
+ if (!RB_TYPE_P(self, T_OBJECT)) { /* insurance to avoid type crash */
+ rb_raise(rb_eTypeError, "invalid instance type");
+ }
+ RBASIC_SET_CLASS(self, klass);
+ }
}
else {
- rb_scan_args(argc, argv, "02", &mesg, &func);
- error = rb_const_get(klass, id_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)) {
- VALUE str = StringValue(mesg);
+ VALUE str = StringValue(mesg);
- if (!NIL_P(func)) rb_str_catf(errmsg, " @ %"PRIsVALUE, func);
- rb_str_catf(errmsg, " - %"PRIsVALUE, str);
+ if (!NIL_P(func)) rb_str_catf(errmsg, " @ %"PRIsVALUE, func);
+ rb_str_catf(errmsg, " - %"PRIsVALUE, str);
}
mesg = errmsg;
@@ -2412,18 +3177,16 @@ syserr_eqq(VALUE self, VALUE exc)
VALUE num, e;
if (!rb_obj_is_kind_of(exc, rb_eSystemCallError)) {
- if (!rb_respond_to(exc, id_errno)) return Qfalse;
+ if (!rb_respond_to(exc, id_errno)) return Qfalse;
}
else if (self == rb_eSystemCallError) return Qtrue;
num = rb_attr_get(exc, id_errno);
if (NIL_P(num)) {
- num = rb_funcallv(exc, id_errno, 0, 0);
+ num = rb_funcallv(exc, id_errno, 0, 0);
}
e = rb_const_get(self, id_Errno);
- if (FIXNUM_P(num) ? num == e : rb_equal(num, e))
- return Qtrue;
- return Qfalse;
+ return RBOOL(FIXNUM_P(num) ? num == e : rb_equal(num, e));
}
@@ -2645,7 +3408,7 @@ syserr_eqq(VALUE self, VALUE exc)
*
* <em>raises the exception:</em>
*
- * NoMethodError: undefined method `to_ary' for "hello":String
+ * NoMethodError: undefined method `to_ary' for an instance of String
*/
/*
@@ -2716,9 +3479,21 @@ syserr_eqq(VALUE self, VALUE exc)
*/
/*
+ * Document-class: NoMatchingPatternError
+ *
+ * Raised when matching pattern not found.
+ */
+
+/*
+ * Document-class: NoMatchingPatternKeyError
+ *
+ * Raised when matching key not found.
+ */
+
+/*
* Document-class: fatal
*
- * fatal is an Exception that is raised when Ruby has encountered a fatal
+ * +fatal+ is an Exception that is raised when Ruby has encountered a fatal
* error and must exit.
*/
@@ -2730,62 +3505,26 @@ syserr_eqq(VALUE self, VALUE exc)
/*
* Document-class: Exception
*
- * \Class Exception and its subclasses are used to communicate between
- * Kernel#raise and +rescue+ statements in <code>begin ... end</code> blocks.
- *
- * 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), 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.
- *
- * 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 +Exception+ and its subclasses are used to indicate that an error
+ * or other problem has occurred,
+ * and may need to be handled.
+ * See {Exceptions}[rdoc-ref:exceptions.md].
*
- * class MyLibrary
- * class Error < ::StandardError
- * end
- *
- * class WidgetError < Error
- * end
- *
- * class FrobError < Error
- * end
- *
- * end
+ * An +Exception+ object carries certain information:
*
- * To handle both MyLibrary::WidgetError and MyLibrary::FrobError the library
- * user can rescue MyLibrary::Error.
+ * - The type (the exception's class),
+ * commonly StandardError, RuntimeError, or a subclass of one or the other;
+ * see {Built-In Exception Class Hierarchy}[rdoc-ref:Exception@Built-In+Exception+Class+Hierarchy].
+ * - An optional descriptive message;
+ * see methods ::new, #message.
+ * - Optional backtrace information;
+ * see methods #backtrace, #backtrace_locations, #set_backtrace.
+ * - An optional cause;
+ * see method #cause.
*
- * == Built-In Exception Classes
+ * == Built-In \Exception Class Hierarchy
*
- * The built-in subclasses of Exception are:
+ * The hierarchy of built-in subclasses of class +Exception+:
*
* * NoMemoryError
* * ScriptError
@@ -2815,13 +3554,14 @@ syserr_eqq(VALUE self, VALUE exc)
* * RuntimeError
* * FrozenError
* * SystemCallError
- * * Errno::*
+ * * Errno (and its subclasses, representing system errors)
* * ThreadError
* * TypeError
* * ZeroDivisionError
* * SystemExit
* * SystemStackError
- * * fatal
+ * * {fatal}[rdoc-ref:fatal]
+ *
*/
static VALUE
@@ -2841,12 +3581,14 @@ exception_dumper(VALUE exc)
}
static int
-ivar_copy_i(st_data_t key, st_data_t val, st_data_t exc)
+ivar_copy_i(ID key, VALUE val, st_data_t exc)
{
- rb_ivar_set((VALUE) exc, (ID) key, (VALUE) val);
+ rb_ivar_set((VALUE)exc, key, val);
return ST_CONTINUE;
}
+void rb_exc_check_circular_cause(VALUE exc);
+
static VALUE
exception_loader(VALUE exc, VALUE obj)
{
@@ -2861,6 +3603,8 @@ exception_loader(VALUE exc, VALUE obj)
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);
}
@@ -2881,6 +3625,7 @@ Init_Exception(void)
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, "detailed_message", exc_detailed_message, -1);
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);
@@ -2911,9 +3656,16 @@ Init_Exception(void)
rb_eSyntaxError = rb_define_class("SyntaxError", rb_eScriptError);
rb_define_method(rb_eSyntaxError, "initialize", syntax_error_initialize, -1);
+ /* RDoc will use literal name value while parsing rb_attr,
+ * and will render `idPath` as an attribute name without this trick */
+ ID path = idPath;
+
+ /* the path that failed to parse */
+ rb_attr(rb_eSyntaxError, path, TRUE, FALSE, FALSE);
+
rb_eLoadError = rb_define_class("LoadError", rb_eScriptError);
- /* the path failed to load */
- rb_attr(rb_eLoadError, rb_intern_const("path"), 1, 0, Qfalse);
+ /* the path that failed to load */
+ rb_attr(rb_eLoadError, path, TRUE, FALSE, FALSE);
rb_eNotImpError = rb_define_class("NotImplementedError", rb_eScriptError);
@@ -2959,6 +3711,7 @@ Init_Exception(void)
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_singleton_method(rb_mWarning, "categories", rb_warning_s_categories, 0);
rb_define_method(rb_mWarning, "warn", rb_warning_s_warn, -1);
rb_extend_object(rb_mWarning, rb_mWarning);
@@ -2968,6 +3721,7 @@ Init_Exception(void)
id_cause = rb_intern_const("cause");
id_message = rb_intern_const("message");
+ id_detailed_message = rb_intern_const("detailed_message");
id_backtrace = rb_intern_const("backtrace");
id_key = rb_intern_const("key");
id_matchee = rb_intern_const("matchee");
@@ -2982,21 +3736,28 @@ Init_Exception(void)
id_category = rb_intern_const("category");
id_deprecated = rb_intern_const("deprecated");
id_experimental = rb_intern_const("experimental");
+ id_performance = rb_intern_const("performance");
+ id_strict_unused_block = rb_intern_const("strict_unused_block");
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);
+ sym_highlight = ID2SYM(rb_intern_const("highlight"));
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);
+ st_add_direct(warning_categories.id2enum, id_performance, RB_WARN_CATEGORY_PERFORMANCE);
+ st_add_direct(warning_categories.id2enum, id_strict_unused_block, RB_WARN_CATEGORY_STRICT_UNUSED_BLOCK);
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);
+ st_add_direct(warning_categories.enum2id, RB_WARN_CATEGORY_PERFORMANCE, id_performance);
+ st_add_direct(warning_categories.enum2id, RB_WARN_CATEGORY_STRICT_UNUSED_BLOCK, id_strict_unused_block);
}
void
@@ -3019,12 +3780,13 @@ rb_vraise(VALUE exc, const char *fmt, va_list ap)
}
void
-rb_raise(VALUE exc, const char *fmt, ...)
+rb_raise(VALUE exc_class, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
- rb_vraise(exc, fmt, args);
+ VALUE exc = rb_exc_new3(exc_class, rb_vsprintf(fmt, args));
va_end(args);
+ rb_exc_raise(exc);
}
NORETURN(static void raise_loaderror(VALUE path, VALUE mesg));
@@ -3065,8 +3827,8 @@ void
rb_notimplement(void)
{
rb_raise(rb_eNotImpError,
- "%"PRIsVALUE"() function is unimplemented on this machine",
- rb_id2str(rb_frame_this_func()));
+ "%"PRIsVALUE"() function is unimplemented on this machine",
+ rb_id2str(rb_frame_this_func()));
}
void
@@ -3079,7 +3841,7 @@ rb_fatal(const char *fmt, ...)
/* 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();
+ rb_print_backtrace(stderr);
die();
}
@@ -3097,7 +3859,7 @@ make_errno_exc(const char *mesg)
errno = 0;
if (n == 0) {
- rb_bug("rb_sys_fail(%s) - errno == 0", mesg ? mesg : "");
+ rb_bug("rb_sys_fail(%s) - errno == 0", mesg ? mesg : "");
}
return rb_syserr_new(n, mesg);
}
@@ -3110,8 +3872,8 @@ make_errno_exc_str(VALUE mesg)
errno = 0;
if (!mesg) mesg = Qnil;
if (n == 0) {
- const char *s = !NIL_P(mesg) ? RSTRING_PTR(mesg) : "";
- rb_bug("rb_sys_fail_str(%s) - errno == 0", s);
+ const char *s = !NIL_P(mesg) ? RSTRING_PTR(mesg) : "";
+ rb_bug("rb_sys_fail_str(%s) - errno == 0", s);
}
return rb_syserr_new_str(n, mesg);
}
@@ -3142,12 +3904,14 @@ rb_syserr_fail_str(int e, VALUE mesg)
rb_exc_raise(rb_syserr_new_str(e, mesg));
}
+#undef rb_sys_fail
void
rb_sys_fail(const char *mesg)
{
rb_exc_raise(make_errno_exc(mesg));
}
+#undef rb_sys_fail_str
void
rb_sys_fail_str(VALUE mesg)
{
@@ -3177,10 +3941,10 @@ rb_syserr_new_path_in(const char *func_name, int n, VALUE path)
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);
+ 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);
@@ -3188,36 +3952,41 @@ rb_syserr_new_path_in(const char *func_name, int n, VALUE path)
}
#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
@@ -3233,11 +4002,11 @@ void
rb_sys_warn(const char *fmt, ...)
{
if (!NIL_P(ruby_verbose)) {
- int errno_save = errno;
- with_warning_string(mesg, 0, fmt) {
- syserr_warning(mesg, errno_save);
- }
- errno = errno_save;
+ int errno_save = errno;
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, errno_save);
+ }
+ errno = errno_save;
}
}
@@ -3245,9 +4014,9 @@ void
rb_syserr_warn(int err, const char *fmt, ...)
{
if (!NIL_P(ruby_verbose)) {
- with_warning_string(mesg, 0, fmt) {
- syserr_warning(mesg, err);
- }
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, err);
+ }
}
}
@@ -3255,11 +4024,11 @@ 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;
+ int errno_save = errno;
+ with_warning_string(mesg, enc, fmt) {
+ syserr_warning(mesg, errno_save);
+ }
+ errno = errno_save;
}
}
@@ -3267,9 +4036,9 @@ 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);
- }
+ with_warning_string(mesg, enc, fmt) {
+ syserr_warning(mesg, err);
+ }
}
}
#endif
@@ -3278,11 +4047,11 @@ 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;
+ int errno_save = errno;
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, errno_save);
+ }
+ errno = errno_save;
}
}
@@ -3291,9 +4060,9 @@ void
rb_syserr_warning(int err, const char *fmt, ...)
{
if (RTEST(ruby_verbose)) {
- with_warning_string(mesg, 0, fmt) {
- syserr_warning(mesg, err);
- }
+ with_warning_string(mesg, 0, fmt) {
+ syserr_warning(mesg, err);
+ }
}
}
#endif
@@ -3302,11 +4071,11 @@ 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;
+ int errno_save = errno;
+ with_warning_string(mesg, enc, fmt) {
+ syserr_warning(mesg, errno_save);
+ }
+ errno = errno_save;
}
}
@@ -3314,9 +4083,9 @@ 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);
- }
+ with_warning_string(mesg, enc, fmt) {
+ syserr_warning(mesg, err);
+ }
}
}
@@ -3361,61 +4130,107 @@ inspect_frozen_obj(VALUE obj, VALUE mesg, int recur)
return mesg;
}
+static VALUE
+get_created_info(VALUE obj, int *pline)
+{
+ VALUE info = rb_attr_get(obj, id_debug_created_info);
+
+ if (NIL_P(info)) return Qnil;
+
+ VALUE path = rb_ary_entry(info, 0);
+ VALUE line = rb_ary_entry(info, 1);
+ if (NIL_P(path)) return Qnil;
+ *pline = NUM2INT(line);
+ return StringValue(path);
+}
+
void
rb_error_frozen_object(VALUE frozen_obj)
{
- VALUE debug_info;
- const ID created_info = id_debug_created_info;
+ rb_yjit_lazy_push_frame(GET_EC()->cfp->pc);
+
VALUE mesg = rb_sprintf("can't modify frozen %"PRIsVALUE": ",
- CLASS_OF(frozen_obj));
+ rb_obj_class(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);
+ int created_line;
+ VALUE created_path = get_created_info(frozen_obj, &created_line);
+ if (!NIL_P(created_path)) {
+ rb_str_catf(mesg, ", created at %"PRIsVALUE":%d", created_path, created_line);
}
rb_exc_raise(exc);
}
-#undef rb_check_frozen
void
-rb_check_frozen(VALUE obj)
+rb_warn_unchilled_literal(VALUE obj)
{
- rb_check_frozen_internal(obj);
+ rb_warning_category_t category = RB_WARN_CATEGORY_DEPRECATED;
+ if (!NIL_P(ruby_verbose) && rb_warning_category_enabled_p(category)) {
+ int line;
+ VALUE file = rb_source_location(&line);
+ VALUE mesg = NIL_P(file) ? rb_str_new(0, 0) : rb_str_dup(file);
+
+ if (!NIL_P(file)) {
+ if (line) rb_str_catf(mesg, ":%d", line);
+ rb_str_cat2(mesg, ": ");
+ }
+ rb_str_cat2(mesg, "warning: literal string will be frozen in the future");
+
+ VALUE str = obj;
+ if (STR_SHARED_P(str)) {
+ str = RSTRING(obj)->as.heap.aux.shared;
+ }
+ VALUE created = get_created_info(str, &line);
+ if (NIL_P(created)) {
+ rb_str_cat2(mesg, " (run with --debug-frozen-string-literal for more information)\n");
+ }
+ else {
+ rb_str_cat2(mesg, "\n");
+ rb_str_append(mesg, created);
+ if (line) rb_str_catf(mesg, ":%d", line);
+ rb_str_cat2(mesg, ": info: the string was created here\n");
+ }
+ rb_warn_category(mesg, rb_warning_category_to_name(category));
+ }
}
void
-rb_error_untrusted(VALUE obj)
+rb_warn_unchilled_symbol_to_s(VALUE obj)
{
- rb_warn_deprecated_to_remove_at(3.2, "rb_error_untrusted", NULL);
+ rb_category_warn(
+ RB_WARN_CATEGORY_DEPRECATED,
+ "string returned by :%s.to_s will be frozen in the future", RSTRING_PTR(obj)
+ );
}
-#undef rb_check_trusted
+#undef rb_check_frozen
void
-rb_check_trusted(VALUE obj)
+rb_check_frozen(VALUE obj)
{
- rb_warn_deprecated_to_remove_at(3.2, "rb_check_trusted", NULL);
+ rb_check_frozen_inline(obj);
}
void
rb_check_copyable(VALUE obj, VALUE orig)
{
if (!FL_ABLE(obj)) return;
- rb_check_frozen_internal(obj);
+ rb_check_frozen(obj);
if (!FL_ABLE(orig)) return;
}
void
Init_syserr(void)
{
- rb_eNOERROR = set_syserr(0, "NOERROR");
+ rb_eNOERROR = setup_syserr(0, "NOERROR");
+#if 0
+ /* No error */
+ rb_define_const(rb_mErrno, "NOERROR", rb_eNOERROR);
+#endif
#define defined_error(name, num) set_syserr((num), (name));
-#define undefined_error(name) set_syserr(0, (name));
+#define undefined_error(name) rb_define_const(rb_mErrno, (name), rb_eNOERROR);
#include "known_errors.inc"
#undef defined_error
#undef undefined_error