1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/error.c
k0kubun ed935aa5be mjit_compile.c: merge initial JIT compiler
which has been developed by Takashi Kokubun <takashikkbn@gmail> as
YARV-MJIT. Many of its bugs are fixed by wanabe <s.wanabe@gmail.com>.

This JIT compiler is designed to be a safe migration path to introduce
JIT compiler to MRI. So this commit does not include any bytecode
changes or dynamic instruction modifications, which are done in original
MJIT.

This commit even strips off some aggressive optimizations from
YARV-MJIT, and thus it's slower than YARV-MJIT too. But it's still
fairly faster than Ruby 2.5 in some benchmarks (attached below).

Note that this JIT compiler passes `make test`, `make test-all`, `make
test-spec` without JIT, and even with JIT. Not only it's perfectly safe
with JIT disabled because it does not replace VM instructions unlike
MJIT, but also with JIT enabled it stably runs Ruby applications
including Rails applications.

I'm expecting this version as just "initial" JIT compiler. I have many
optimization ideas which are skipped for initial merging, and you may
easily replace this JIT compiler with a faster one by just replacing
mjit_compile.c. `mjit_compile` interface is designed for the purpose.

common.mk: update dependencies for mjit_compile.c.

internal.h: declare `rb_vm_insn_addr2insn` for MJIT.

vm.c: exclude some definitions if `-DMJIT_HEADER` is provided to
compiler. This avoids to include some functions which take a long time
to compile, e.g. vm_exec_core. Some of the purpose is achieved in
transform_mjit_header.rb (see `IGNORED_FUNCTIONS`) but others are
manually resolved for now. Load mjit_helper.h for MJIT header.
mjit_helper.h: New. This is a file used only by JIT-ed code. I'll
refactor `mjit_call_cfunc` later.
vm_eval.c: add some #ifdef switches to skip compiling some functions
like Init_vm_eval.

win32/mkexports.rb: export thread/ec functions, which are used by MJIT.

include/ruby/defines.h: add MJIT_FUNC_EXPORTED macro alis to clarify
that a function is exported only for MJIT.

array.c: export a function used by MJIT.
bignum.c: ditto.
class.c: ditto.
compile.c: ditto.
error.c: ditto.
gc.c: ditto.
hash.c: ditto.
iseq.c: ditto.
numeric.c: ditto.
object.c: ditto.
proc.c: ditto.
re.c: ditto.
st.c: ditto.
string.c: ditto.
thread.c: ditto.
variable.c: ditto.
vm_backtrace.c: ditto.
vm_insnhelper.c: ditto.
vm_method.c: ditto.

I would like to improve maintainability of function exports, but I
believe this way is acceptable as initial merging if we clarify the
new exports are for MJIT (so that we can use them as TODO list to fix)
and add unit tests to detect unresolved symbols.
I'll add unit tests of JIT compilations in succeeding commits.

Author: Takashi Kokubun <takashikkbn@gmail.com>
Contributor: wanabe <s.wanabe@gmail.com>

Part of [Feature #14235]

---

* Known issues
  * Code generated by gcc is faster than clang. The benchmark may be worse
    in macOS. Following benchmark result is provided by gcc w/ Linux.
  * Performance is decreased when Google Chrome is running
  * JIT can work on MinGW, but it doesn't improve performance at least
    in short running benchmark.
  * Currently it doesn't perform well with Rails. We'll try to fix this
    before release.

---

* Benchmark reslts

Benchmarked with:
Intel 4.0GHz i7-4790K with 16GB memory under x86-64 Ubuntu 8 Cores

- 2.0.0-p0: Ruby 2.0.0-p0
- r62186: Ruby trunk (early 2.6.0), before MJIT changes
- JIT off: On this commit, but without `--jit` option
- JIT on: On this commit, and with `--jit` option

** Optcarrot fps

Benchmark: https://github.com/mame/optcarrot

|         |2.0.0-p0 |r62186   |JIT off  |JIT on   |
|:--------|:--------|:--------|:--------|:--------|
|fps      |37.32    |51.46    |51.31    |58.88    |
|vs 2.0.0 |1.00x    |1.38x    |1.37x    |1.58x    |

** MJIT benchmarks

Benchmark: https://github.com/benchmark-driver/mjit-benchmarks
(Original: https://github.com/vnmakarov/ruby/tree/rtl_mjit_branch/MJIT-benchmarks)

|           |2.0.0-p0 |r62186   |JIT off  |JIT on   |
|:----------|:--------|:--------|:--------|:--------|
|aread      |1.00     |1.09     |1.07     |2.19     |
|aref       |1.00     |1.13     |1.11     |2.22     |
|aset       |1.00     |1.50     |1.45     |2.64     |
|awrite     |1.00     |1.17     |1.13     |2.20     |
|call       |1.00     |1.29     |1.26     |2.02     |
|const2     |1.00     |1.10     |1.10     |2.19     |
|const      |1.00     |1.11     |1.10     |2.19     |
|fannk      |1.00     |1.04     |1.02     |1.00     |
|fib        |1.00     |1.32     |1.31     |1.84     |
|ivread     |1.00     |1.13     |1.12     |2.43     |
|ivwrite    |1.00     |1.23     |1.21     |2.40     |
|mandelbrot |1.00     |1.13     |1.16     |1.28     |
|meteor     |1.00     |2.97     |2.92     |3.17     |
|nbody      |1.00     |1.17     |1.15     |1.49     |
|nest-ntimes|1.00     |1.22     |1.20     |1.39     |
|nest-while |1.00     |1.10     |1.10     |1.37     |
|norm       |1.00     |1.18     |1.16     |1.24     |
|nsvb       |1.00     |1.16     |1.16     |1.17     |
|red-black  |1.00     |1.02     |0.99     |1.12     |
|sieve      |1.00     |1.30     |1.28     |1.62     |
|trees      |1.00     |1.14     |1.13     |1.19     |
|while      |1.00     |1.12     |1.11     |2.41     |

** Discourse's script/bench.rb

Benchmark: https://github.com/discourse/discourse/blob/v1.8.7/script/bench.rb

NOTE: Rails performance was somehow a little degraded with JIT for now.
We should fix this.
(At least I know opt_aref is performing badly in JIT and I have an idea
 to fix it. Please wait for the fix.)

*** JIT off
Your Results: (note for timings- percentile is first, duration is second in millisecs)

categories_admin:
  50: 17
  75: 18
  90: 22
  99: 29
home_admin:
  50: 21
  75: 21
  90: 27
  99: 40
topic_admin:
  50: 17
  75: 18
  90: 22
  99: 32
categories:
  50: 35
  75: 41
  90: 43
  99: 77
home:
  50: 39
  75: 46
  90: 49
  99: 95
topic:
  50: 46
  75: 52
  90: 56
  99: 101

*** JIT on
Your Results: (note for timings- percentile is first, duration is second in millisecs)

categories_admin:
  50: 19
  75: 21
  90: 25
  99: 33
home_admin:
  50: 24
  75: 26
  90: 30
  99: 35
topic_admin:
  50: 19
  75: 20
  90: 25
  99: 30
categories:
  50: 40
  75: 44
  90: 48
  99: 76
home:
  50: 42
  75: 48
  90: 51
  99: 89
topic:
  50: 49
  75: 55
  90: 58
  99: 99

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62197 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2018-02-04 11:22:28 +00:00

2818 lines
65 KiB
C

/**********************************************************************
error.c -
$Author$
created at: Mon Aug 9 16:11:34 JST 1993
Copyright (C) 1993-2007 Yukihiro Matsumoto
**********************************************************************/
#include "ruby/encoding.h"
#include "ruby/st.h"
#include "internal.h"
#include "ruby_assert.h"
#include "vm_core.h"
#include <stdio.h>
#include <stdarg.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#if defined __APPLE__
# include <AvailabilityMacros.h>
#endif
/*!
* \defgroup exception Exception handlings
* \{
*/
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif
#ifndef WIFEXITED
#define WIFEXITED(status) 1
#endif
#ifndef WEXITSTATUS
#define WEXITSTATUS(status) (status)
#endif
VALUE rb_iseqw_local_variables(VALUE iseqval);
VALUE rb_iseqw_new(const rb_iseq_t *);
int rb_str_end_with_asciichar(VALUE str, int c);
VALUE rb_eEAGAIN;
VALUE rb_eEWOULDBLOCK;
VALUE rb_eEINPROGRESS;
static VALUE rb_mWarning;
static VALUE rb_cWarningBuffer;
static ID id_warn;
extern const char ruby_description[];
static const char REPORTBUG_MSG[] =
"[NOTE]\n" \
"You may have encountered a bug in the Ruby interpreter" \
" or extension libraries.\n" \
"Bug reports are welcome.\n" \
""
"For details: http://www.ruby-lang.org/bugreport.html\n\n" \
;
static const char *
rb_strerrno(int err)
{
#define defined_error(name, num) if (err == (num)) return (name);
#define undefined_error(name)
#include "known_errors.inc"
#undef defined_error
#undef undefined_error
return NULL;
}
static int
err_position_0(char *buf, long len, const char *file, int line)
{
if (!file) {
return 0;
}
else if (line == 0) {
return snprintf(buf, len, "%s: ", file);
}
else {
return snprintf(buf, len, "%s:%d: ", file, line);
}
}
static VALUE
err_vcatf(VALUE str, const char *pre, const char *file, int line,
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, ": ");
}
if (pre) rb_str_cat2(str, pre);
rb_str_vcatf(str, fmt, args);
return str;
}
VALUE
rb_syntax_error_append(VALUE exc, VALUE file, int line, int column,
rb_encoding *enc, const char *fmt, va_list args)
{
const char *fn = NIL_P(file) ? NULL : RSTRING_PTR(file);
if (!exc) {
VALUE mesg = rb_enc_str_new(0, 0, enc);
err_vcatf(mesg, NULL, fn, line, fmt, args);
rb_str_cat2(mesg, "\n");
rb_write_error_str(mesg);
}
else {
VALUE mesg;
if (NIL_P(exc)) {
mesg = rb_enc_str_new(0, 0, enc);
exc = rb_class_new_instance(1, &mesg, rb_eSyntaxError);
}
else {
mesg = rb_attr_get(exc, idMesg);
if (RSTRING_LEN(mesg) > 0 && *(RSTRING_END(mesg)-1) != '\n')
rb_str_cat_cstr(mesg, "\n");
}
err_vcatf(mesg, NULL, fn, line, fmt, args);
}
return exc;
}
void
ruby_deprecated_internal_feature(const char *func)
{
rb_print_backtrace();
rb_fatal("%s is only for internal use and deprecated; do not use", func);
}
/*
* call-seq:
* warn(msg) -> nil
*
* Writes warning message +msg+ to $stderr, followed by a newline
* if the message does not end in a newline. This method is called
* by Ruby for all emitted warnings.
*/
static VALUE
rb_warning_s_warn(VALUE mod, VALUE str)
{
Check_Type(str, T_STRING);
rb_must_asciicompat(str);
rb_write_error_str(str);
return Qnil;
}
/*
* Document-module: Warning
*
* The Warning module contains a single method named #warn, and the
* module extends itself, making <code>Warning.warn</code> available.
* Warning.warn is called for all warnings issued by Ruby.
* By default, warnings are printed to $stderr.
*
* By overriding Warning.warn, you can change how warnings are
* handled by Ruby, either filtering some warnings, and/or outputting
* warnings somewhere other than $stderr. When Warning.warn is
* overridden, super can be called to get the default behavior of
* printing the warning to $stderr.
*/
VALUE
rb_warning_warn(VALUE mod, VALUE str)
{
return rb_funcallv(mod, id_warn, 1, &str);
}
static void
rb_write_warning_str(VALUE str)
{
rb_warning_warn(rb_mWarning, str);
}
static VALUE
warn_vsprintf(rb_encoding *enc, const char *file, int line, const char *fmt, va_list args)
{
VALUE str = rb_enc_str_new(0, 0, enc);
err_vcatf(str, "warning: ", file, line, fmt, args);
return rb_str_cat2(str, "\n");
}
void
rb_compile_warn(const char *file, int line, const char *fmt, ...)
{
VALUE str;
va_list args;
if (NIL_P(ruby_verbose)) return;
va_start(args, fmt);
str = warn_vsprintf(NULL, file, line, fmt, args);
va_end(args);
rb_write_warning_str(str);
}
/* rb_compile_warning() reports only in verbose mode */
void
rb_compile_warning(const char *file, int line, const char *fmt, ...)
{
VALUE str;
va_list args;
if (!RTEST(ruby_verbose)) return;
va_start(args, fmt);
str = warn_vsprintf(NULL, file, line, fmt, args);
va_end(args);
rb_write_warning_str(str);
}
static VALUE
warning_string(rb_encoding *enc, const char *fmt, va_list args)
{
int line;
const char *file = rb_source_location_cstr(&line);
return warn_vsprintf(enc, file, line, fmt, args);
}
#define with_warning_string(mesg, enc, fmt) \
VALUE mesg; \
va_list args; va_start(args, fmt); \
mesg = warning_string(enc, fmt, args); \
va_end(args);
void
rb_warn(const char *fmt, ...)
{
if (!NIL_P(ruby_verbose)) {
with_warning_string(mesg, 0, fmt) {
rb_write_warning_str(mesg);
}
}
}
void
rb_enc_warn(rb_encoding *enc, const char *fmt, ...)
{
if (!NIL_P(ruby_verbose)) {
with_warning_string(mesg, enc, fmt) {
rb_write_warning_str(mesg);
}
}
}
/* rb_warning() reports only in verbose mode */
void
rb_warning(const char *fmt, ...)
{
if (RTEST(ruby_verbose)) {
with_warning_string(mesg, 0, fmt) {
rb_write_warning_str(mesg);
}
}
}
VALUE
rb_warning_string(const char *fmt, ...)
{
with_warning_string(mesg, 0, fmt) {
}
return mesg;
}
#if 0
void
rb_enc_warning(rb_encoding *enc, const char *fmt, ...)
{
if (RTEST(ruby_verbose)) {
with_warning_string(mesg, enc, fmt) {
rb_write_warning_str(mesg);
}
}
}
#endif
static inline int
end_with_asciichar(VALUE str, int c)
{
return RB_TYPE_P(str, T_STRING) &&
rb_str_end_with_asciichar(str, c);
}
static VALUE
warning_write(int argc, VALUE *argv, VALUE buf)
{
while (argc-- > 0) {
rb_str_append(buf, *argv++);
}
return buf;
}
/*
* call-seq:
* warn(msg, ...) -> nil
*
* If warnings have been disabled (for example with the
* <code>-W0</code> flag), does nothing. Otherwise,
* converts each of the messages to strings, appends a newline
* character to the string if the string does not end in a newline,
* and calls <code>Warning.warn</code> with the string.
*
* warn("warning 1", "warning 2")
*
* <em>produces:</em>
*
* warning 1
* warning 2
*
* If the <code>:uplevel</code> keyword is given, the string will
* be prepended with information for the given caller frame in
* the same format used by the <code>rb_warn</code> C function.
*
* # In baz.rb
* def foo
* warn("invalid call to foo", uplevel: 1)
* end
*
* def bar
* foo
* end
*
* bar
*
* <em>produces:</em>
*
* baz.rb:6: warning: invalid call to foo
*/
static VALUE
rb_warn_m(int argc, VALUE *argv, VALUE exc)
{
VALUE opts, uplevel = Qnil;
if (!NIL_P(ruby_verbose) && argc > 0 &&
(argc = rb_scan_args(argc, argv, "*:", NULL, &opts)) > 0) {
VALUE str = argv[0];
if (!NIL_P(opts)) {
static ID kwds[1];
if (!kwds[0]) {
CONST_ID(kwds[0], "uplevel");
}
rb_get_kwargs(opts, kwds, 0, 1, &uplevel);
if (uplevel == Qundef) {
uplevel = Qnil;
}
else if (!NIL_P(uplevel)) {
VALUE args[2];
long lev = NUM2LONG(uplevel);
if (lev < 0) {
rb_raise(rb_eArgError, "negative level (%ld)", lev);
}
args[0] = LONG2NUM(lev + 1);
args[1] = INT2FIX(1);
uplevel = rb_vm_thread_backtrace_locations(2, args, GET_THREAD()->self);
if (!NIL_P(uplevel)) {
uplevel = rb_ary_entry(uplevel, 0);
}
}
}
if (argc > 1 || !NIL_P(uplevel) || !end_with_asciichar(str, '\n')) {
if (NIL_P(uplevel)) {
str = rb_str_tmp_new(0);
}
else {
VALUE path;
path = rb_funcall(uplevel, rb_intern("path"), 0);
str = rb_sprintf("%s:%ld: warning: ",
rb_string_value_ptr(&path),
NUM2LONG(rb_funcall(uplevel, rb_intern("lineno"), 0)));
}
RBASIC_SET_CLASS(str, rb_cWarningBuffer);
rb_io_puts(argc, argv, str);
RBASIC_SET_CLASS(str, rb_cString);
}
if (exc == rb_mWarning) {
rb_must_asciicompat(str);
rb_write_error_str(str);
}
else {
rb_write_warning_str(str);
}
}
return Qnil;
}
#define MAX_BUG_REPORTERS 0x100
static struct bug_reporters {
void (*func)(FILE *out, void *data);
void *data;
} bug_reporters[MAX_BUG_REPORTERS];
static int bug_reporters_size;
int
rb_bug_reporter_add(void (*func)(FILE *, void *), void *data)
{
struct bug_reporters *reporter;
if (bug_reporters_size >= MAX_BUG_REPORTERS) {
return 0; /* failed to register */
}
reporter = &bug_reporters[bug_reporters_size++];
reporter->func = func;
reporter->data = data;
return 1;
}
/* SIGSEGV handler might have a very small stack. Thus we need to use it carefully. */
#define REPORT_BUG_BUFSIZ 256
static FILE *
bug_report_file(const char *file, int line)
{
char buf[REPORT_BUG_BUFSIZ];
FILE *out = stderr;
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;
}
return NULL;
}
FUNC_MINIMIZED(static void bug_important_message(FILE *out, const char *const msg, size_t len));
static void
bug_important_message(FILE *out, const char *const msg, size_t len)
{
const char *const endmsg = msg + len;
const char *p = msg;
if (!len) return;
if (isatty(fileno(out))) {
static const char red[] = "\033[;31;1;7m";
static const char green[] = "\033[;32;7m";
static const char reset[] = "\033[m";
const char *e = strchr(p, '\n');
const int w = (int)(e - p);
do {
int i = (int)(e - p);
fputs(*p == ' ' ? green : red, out);
fwrite(p, 1, e - p, out);
for (; i < w; ++i) fputc(' ', out);
fputs(reset, out);
fputc('\n', out);
} while ((p = e + 1) < endmsg && (e = strchr(p, '\n')) != 0 && e > p + 1);
}
fwrite(p, 1, endmsg - p, out);
}
static void
preface_dump(FILE *out)
{
#if defined __APPLE__
static const char msg[] = ""
"-- Crash Report log information "
"--------------------------------------------\n"
" See Crash Report log file under the one of following:\n"
# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6
" * ~/Library/Logs/CrashReporter\n"
" * /Library/Logs/CrashReporter\n"
# endif
" * ~/Library/Logs/DiagnosticReports\n"
" * /Library/Logs/DiagnosticReports\n"
" for more details.\n"
"Don't forget to include the above Crash Report log file in bug reports.\n"
"\n";
const size_t msglen = sizeof(msg) - 1;
#else
const char *msg = NULL;
const size_t msglen = 0;
#endif
bug_important_message(out, msg, msglen);
}
static void
postscript_dump(FILE *out)
{
#if defined __APPLE__
static const char msg[] = ""
"[IMPORTANT]"
/*" ------------------------------------------------"*/
"\n""Don't forget to include the Crash Report log file under\n"
# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6
"CrashReporter or "
# endif
"DiagnosticReports directory in bug reports.\n"
/*"------------------------------------------------------------\n"*/
"\n";
const size_t msglen = sizeof(msg) - 1;
#else
const char *msg = NULL;
const size_t msglen = 0;
#endif
bug_important_message(out, msg, msglen);
}
static void
bug_report_begin_valist(FILE *out, const char *fmt, va_list args)
{
char buf[REPORT_BUG_BUFSIZ];
fputs("[BUG] ", out);
vsnprintf(buf, sizeof(buf), fmt, args);
fputs(buf, out);
snprintf(buf, sizeof(buf), "\n%s\n\n", ruby_description);
fputs(buf, out);
preface_dump(out);
}
#define bug_report_begin(out, fmt) do { \
va_list args; \
va_start(args, fmt); \
bug_report_begin_valist(out, fmt, args); \
va_end(args); \
} while (0)
static void
bug_report_end(FILE *out)
{
/* call additional bug reporters */
{
int i;
for (i=0; i<bug_reporters_size; i++) {
struct bug_reporters *reporter = &bug_reporters[i];
(*reporter->func)(out, reporter->data);
}
}
fputs(REPORTBUG_MSG, out);
postscript_dump(out);
}
#define report_bug(file, line, fmt, ctx) do { \
FILE *out = bug_report_file(file, line); \
if (out) { \
bug_report_begin(out, fmt); \
rb_vm_bugreport(ctx); \
bug_report_end(out); \
} \
} while (0) \
#define report_bug_valist(file, line, fmt, ctx, args) do { \
FILE *out = bug_report_file(file, line); \
if (out) { \
bug_report_begin_valist(out, fmt, args); \
rb_vm_bugreport(ctx); \
bug_report_end(out); \
} \
} while (0) \
NORETURN(static void die(void));
static void
die(void)
{
#if defined(_WIN32) && defined(RUBY_MSVCRT_VERSION) && RUBY_MSVCRT_VERSION >= 80
_set_abort_behavior( 0, _CALL_REPORTFAULT);
#endif
abort();
}
void
rb_bug(const char *fmt, ...)
{
const char *file = NULL;
int line = 0;
if (GET_EC()) {
file = rb_source_location_cstr(&line);
}
report_bug(file, line, fmt, NULL);
die();
}
void
rb_bug_context(const void *ctx, const char *fmt, ...)
{
const char *file = NULL;
int line = 0;
if (GET_EC()) {
file = rb_source_location_cstr(&line);
}
report_bug(file, line, fmt, ctx);
die();
}
void
rb_bug_errno(const char *mesg, int errno_arg)
{
if (errno_arg == 0)
rb_bug("%s: errno == 0 (NOERROR)", mesg);
else {
const char *errno_str = rb_strerrno(errno_arg);
if (errno_str)
rb_bug("%s: %s (%s)", mesg, strerror(errno_arg), errno_str);
else
rb_bug("%s: %s (%d)", mesg, strerror(errno_arg), errno_arg);
}
}
/*
* this is safe to call inside signal handler and timer thread
* (which isn't a Ruby Thread object)
*/
#define write_or_abort(fd, str, len) (write((fd), (str), (len)) < 0 ? abort() : (void)0)
#define WRITE_CONST(fd,str) write_or_abort((fd),(str),sizeof(str) - 1)
void
rb_async_bug_errno(const char *mesg, int errno_arg)
{
WRITE_CONST(2, "[ASYNC BUG] ");
write_or_abort(2, mesg, strlen(mesg));
WRITE_CONST(2, "\n");
if (errno_arg == 0) {
WRITE_CONST(2, "errno == 0 (NOERROR)\n");
}
else {
const char *errno_str = rb_strerrno(errno_arg);
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_CONST(2, "\n\n");
WRITE_CONST(2, REPORTBUG_MSG);
abort();
}
void
rb_report_bug_valist(VALUE file, int line, const char *fmt, va_list args)
{
report_bug_valist(RSTRING_PTR(file), line, fmt, NULL, args);
}
void
rb_assert_failure(const char *file, int line, const char *name, const char *expr)
{
FILE *out = stderr;
fprintf(out, "Assertion Failed: %s:%d:", file, line);
if (name) fprintf(out, "%s:", name);
fprintf(out, "%s\n%s\n\n", expr, ruby_description);
preface_dump(out);
rb_vm_bugreport(NULL);
bug_report_end(out);
die();
}
static const char builtin_types[][10] = {
"", /* 0x00, */
"Object",
"Class",
"Module",
"Float",
"String",
"Regexp",
"Array",
"Hash",
"Struct",
"Bignum",
"File",
"Data", /* internal use: wrapped C pointers */
"MatchData", /* data of $~ */
"Complex",
"Rational",
"", /* 0x10 */
"nil",
"true",
"false",
"Symbol", /* :symbol */
"Fixnum",
"undef", /* internal use: #undef; should not happen */
"", /* 0x17 */
"", /* 0x18 */
"", /* 0x19 */
"Memo", /* internal use: general memo */
"Node", /* internal use: syntax tree node */
"iClass", /* internal use: mixed-in module holder */
};
const char *
rb_builtin_type_name(int t)
{
const char *name;
if ((unsigned int)t >= numberof(builtin_types)) return 0;
name = builtin_types[t];
if (*name) return name;
return 0;
}
static const char *
builtin_class_name(VALUE x)
{
const char *etype;
if (NIL_P(x)) {
etype = "nil";
}
else if (FIXNUM_P(x)) {
etype = "Integer";
}
else if (SYMBOL_P(x)) {
etype = "Symbol";
}
else if (RB_TYPE_P(x, T_TRUE)) {
etype = "true";
}
else if (RB_TYPE_P(x, T_FALSE)) {
etype = "false";
}
else {
etype = NULL;
}
return etype;
}
const char *
rb_builtin_class_name(VALUE x)
{
const char *etype = builtin_class_name(x);
if (!etype) {
etype = rb_obj_classname(x);
}
return etype;
}
NORETURN(static void unexpected_type(VALUE, int, int));
#define UNDEF_LEAKED "undef leaked to the Ruby space"
static void
unexpected_type(VALUE x, int xt, int t)
{
const char *tname = rb_builtin_type_name(t);
VALUE mesg, exc = rb_eFatal;
if (tname) {
const char *cname = builtin_class_name(x);
if (cname)
mesg = rb_sprintf("wrong argument type %s (expected %s)",
cname, tname);
else
mesg = rb_sprintf("wrong argument type %"PRIsVALUE" (expected %s)",
rb_obj_class(x), tname);
exc = rb_eTypeError;
}
else if (xt > T_MASK && xt <= 0x3f) {
mesg = rb_sprintf("unknown type 0x%x (0x%x given, probably comes"
" from extension library for ruby 1.8)", t, xt);
}
else {
mesg = rb_sprintf("unknown type 0x%x (0x%x given)", t, xt);
}
rb_exc_raise(rb_exc_new_str(exc, mesg));
}
void
rb_check_type(VALUE x, int t)
{
int xt;
if (x == Qundef) {
rb_bug(UNDEF_LEAKED);
}
xt = TYPE(x);
if (xt != t || (xt == T_DATA && RTYPEDDATA_P(x))) {
unexpected_type(x, xt, t);
}
}
void
rb_unexpected_type(VALUE x, int t)
{
if (x == Qundef) {
rb_bug(UNDEF_LEAKED);
}
unexpected_type(x, TYPE(x), t);
}
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;
}
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;
}
void *
rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type)
{
const char *etype;
if (!RB_TYPE_P(obj, T_DATA)) {
wrong_type:
etype = builtin_class_name(obj);
if (!etype)
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected %s)",
rb_obj_class(obj), data_type->wrap_struct_name);
wrong_datatype:
rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)",
etype, data_type->wrap_struct_name);
}
if (!RTYPEDDATA_P(obj)) {
goto wrong_type;
}
else if (!rb_typeddata_inherited_p(RTYPEDDATA_TYPE(obj), data_type)) {
etype = RTYPEDDATA_TYPE(obj)->wrap_struct_name;
goto wrong_datatype;
}
return DATA_PTR(obj);
}
/* exception classes */
VALUE rb_eException;
VALUE rb_eSystemExit;
VALUE rb_eInterrupt;
VALUE rb_eSignal;
VALUE rb_eFatal;
VALUE rb_eStandardError;
VALUE rb_eRuntimeError;
VALUE rb_eFrozenError;
VALUE rb_eTypeError;
VALUE rb_eArgError;
VALUE rb_eIndexError;
VALUE rb_eKeyError;
VALUE rb_eRangeError;
VALUE rb_eNameError;
VALUE rb_eEncodingError;
VALUE rb_eEncCompatError;
VALUE rb_eNoMethodError;
VALUE rb_eSecurityError;
VALUE rb_eNotImpError;
VALUE rb_eNoMemError;
VALUE rb_cNameErrorMesg;
VALUE rb_eScriptError;
VALUE rb_eSyntaxError;
VALUE rb_eLoadError;
VALUE rb_eSystemCallError;
VALUE rb_mErrno;
static VALUE rb_eNOERROR;
static ID id_new, id_cause, id_message, id_backtrace;
static ID id_name, id_key, id_args, id_Errno, id_errno, id_i_path;
static ID id_receiver, id_iseq, id_local_variables;
static ID id_private_call_p;
#define id_bt idBt
#define id_bt_locations idBt_locations
#define id_mesg idMesg
#undef rb_exc_new_cstr
VALUE
rb_exc_new(VALUE etype, const char *ptr, long len)
{
return rb_funcall(etype, id_new, 1, rb_str_new(ptr, len));
}
VALUE
rb_exc_new_cstr(VALUE etype, const char *s)
{
return rb_exc_new(etype, s, strlen(s));
}
VALUE
rb_exc_new_str(VALUE etype, VALUE str)
{
StringValue(str);
return rb_funcall(etype, id_new, 1, str);
}
/*
* call-seq:
* Exception.new(msg = nil) -> exception
*
* Construct a new Exception object, optionally passing in
* a message.
*/
static VALUE
exc_initialize(int argc, VALUE *argv, VALUE exc)
{
VALUE arg;
rb_scan_args(argc, argv, "01", &arg);
rb_ivar_set(exc, id_mesg, arg);
rb_ivar_set(exc, id_bt, Qnil);
return exc;
}
/*
* Document-method: exception
*
* call-seq:
* exc.exception(string) -> an_exception or exc
*
* With no argument, or if the argument is the same as the receiver,
* return the receiver. Otherwise, create a new
* exception object of the same class as the receiver, but with a
* message equal to <code>string.to_str</code>.
*
*/
static VALUE
exc_exception(int argc, VALUE *argv, VALUE self)
{
VALUE exc;
if (argc == 0) return self;
if (argc == 1 && self == argv[0]) return self;
exc = rb_obj_clone(self);
exc_initialize(argc, argv, exc);
return exc;
}
/*
* call-seq:
* exception.to_s -> string
*
* Returns exception's message (or the name of the exception if
* no message is set).
*/
static VALUE
exc_to_s(VALUE exc)
{
VALUE mesg = rb_attr_get(exc, idMesg);
if (NIL_P(mesg)) return rb_class_name(CLASS_OF(exc));
return rb_String(mesg);
}
/* FIXME: Include eval_error.c */
void rb_error_write(VALUE errinfo, VALUE errat, VALUE str);
/*
* call-seq:
* exception.full_message -> string
*
* Returns formatted string of <i>exception</i>.
* The returned string is formatted using the same format that Ruby uses
* when printing an uncaught exceptions to stderr. So it may differ by
* <code>$stderr.tty?</code> at the timing of a call.
*/
static VALUE
exc_full_message(VALUE exc)
{
VALUE str = rb_str_new2("");
VALUE errat = rb_get_backtrace(exc);
rb_error_write(exc, errat, str);
return str;
}
/*
* call-seq:
* exception.message -> string
*
* Returns the result of invoking <code>exception.to_s</code>.
* Normally this returns the exception's message or name.
*/
static VALUE
exc_message(VALUE exc)
{
return rb_funcallv(exc, idTo_s, 0, 0);
}
/*
* call-seq:
* exception.inspect -> string
*
* Return this exception's class name and message
*/
static VALUE
exc_inspect(VALUE exc)
{
VALUE str, klass;
klass = CLASS_OF(exc);
exc = rb_obj_as_string(exc);
if (RSTRING_LEN(exc) == 0) {
return rb_str_dup(rb_class_name(klass));
}
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);
rb_str_buf_cat(str, ">", 1);
return str;
}
/*
* call-seq:
* exception.backtrace -> array
*
* 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
*
* def b
* a()
* end
*
* begin
* b()
* rescue => detail
* print detail.backtrace.join("\n")
* end
*
* <em>produces:</em>
*
* prog.rb:2:in `a'
* prog.rb:6:in `b'
* prog.rb:10
*/
static VALUE
exc_backtrace(VALUE exc)
{
VALUE obj;
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); */
}
return obj;
}
VALUE
rb_get_backtrace(VALUE exc)
{
ID mid = id_backtrace;
if (rb_method_basic_definition_p(CLASS_OF(exc), id_backtrace)) {
VALUE info, 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);
if (NIL_P(info))
return Qnil;
return rb_check_backtrace(info);
}
return rb_funcallv(exc, mid, 0, 0);
}
/*
* call-seq:
* exception.backtrace_locations -> array
*
* Returns any backtrace associated with the exception. This method is
* similar to Exception#backtrace, but the backtrace is an array of
* Thread::Backtrace::Location.
*
* Now, this method is not affected by Exception#set_backtrace().
*/
static VALUE
exc_backtrace_locations(VALUE exc)
{
VALUE obj;
obj = rb_attr_get(exc, id_bt_locations);
if (!NIL_P(obj)) {
obj = rb_backtrace_to_location_ary(obj);
}
return obj;
}
VALUE
rb_check_backtrace(VALUE bt)
{
long i;
static const char err[] = "backtrace must be Array of String";
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);
}
}
}
return bt;
}
/*
* call-seq:
* exc.set_backtrace(backtrace) -> array
*
* 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.
*
*/
static VALUE
exc_set_backtrace(VALUE exc, VALUE bt)
{
return rb_ivar_set(exc, id_bt, rb_check_backtrace(bt));
}
MJIT_FUNC_EXPORTED VALUE
rb_exc_set_backtrace(VALUE exc, VALUE bt)
{
return exc_set_backtrace(exc, bt);
}
/*
* call-seq:
* exception.cause -> an_exception or nil
*
* Returns the previous exception ($!) at the time this exception was raised.
* This is useful for wrapping exceptions and retaining the original exception
* information.
*/
static VALUE
exc_cause(VALUE exc)
{
return rb_attr_get(exc, id_cause);
}
static VALUE
try_convert_to_exception(VALUE obj)
{
return rb_check_funcall(obj, idException, 0, 0);
}
/*
* call-seq:
* exc == obj -> true or false
*
* Equality---If <i>obj</i> is not an <code>Exception</code>, returns
* <code>false</code>. Otherwise, returns <code>true</code> if <i>exc</i> and
* <i>obj</i> share same class, messages, and backtrace.
*/
static VALUE
exc_equal(VALUE exc, VALUE obj)
{
VALUE mesg, backtrace;
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;
}
else {
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;
}
/*
* call-seq:
* SystemExit.new -> system_exit
* SystemExit.new(status) -> system_exit
* SystemExit.new(status, msg) -> system_exit
* SystemExit.new(msg) -> system_exit
*
* Create a new +SystemExit+ exception with the given status and message.
* Status is true, false, or an integer.
* If status is not given, true is used.
*/
static VALUE
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 {
#if EXIT_SUCCESS != 0
if (status == INT2FIX(0))
status = INT2FIX(EXIT_SUCCESS);
#endif
++argv;
--argc;
}
break;
}
}
else {
status = INT2FIX(EXIT_SUCCESS);
}
rb_call_super(argc, argv);
rb_ivar_set(exc, id_status, status);
return exc;
}
/*
* call-seq:
* system_exit.status -> integer
*
* Return the status value associated with this system exit.
*/
static VALUE
exit_status(VALUE exc)
{
return rb_attr_get(exc, id_status);
}
/*
* call-seq:
* system_exit.success? -> true or false
*
* Returns +true+ if exiting successful, +false+ if not.
*/
static VALUE
exit_success_p(VALUE exc)
{
VALUE status_val = rb_attr_get(exc, id_status);
int status;
if (NIL_P(status_val))
return Qtrue;
status = NUM2INT(status_val);
if (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS)
return Qtrue;
return Qfalse;
}
void
rb_name_error(ID id, const char *fmt, ...)
{
VALUE exc, argv[2];
va_list args;
va_start(args, fmt);
argv[0] = rb_vsprintf(fmt, args);
va_end(args);
argv[1] = ID2SYM(id);
exc = rb_class_new_instance(2, argv, rb_eNameError);
rb_exc_raise(exc);
}
void
rb_name_error_str(VALUE str, const char *fmt, ...)
{
VALUE exc, argv[2];
va_list args;
va_start(args, fmt);
argv[0] = rb_vsprintf(fmt, args);
va_end(args);
argv[1] = str;
exc = rb_class_new_instance(2, argv, rb_eNameError);
rb_exc_raise(exc);
}
static VALUE name_err_initialize_options(int argc, VALUE *argv, VALUE self, VALUE options);
/*
* call-seq:
* NameError.new(msg [, name]) -> name_error
*
* Construct a new NameError exception. If given the <i>name</i>
* parameter may subsequently be examined using the <code>NameError.name</code>
* method.
*/
static VALUE
name_err_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE options;
argc = rb_scan_args(argc, argv, "*:", NULL, &options);
return name_err_initialize_options(argc, argv, self, options);
}
static VALUE
name_err_initialize_options(int argc, VALUE *argv, VALUE self, VALUE options)
{
ID keywords[1];
VALUE values[numberof(keywords)];
VALUE name;
VALUE iseqw = Qnil;
int i;
keywords[0] = id_receiver;
rb_get_kwargs(options, keywords, 0, numberof(values), values);
name = (argc > 1) ? argv[--argc] : Qnil;
rb_call_super(argc, argv);
rb_ivar_set(self, id_name, name);
for (i = 0; i < numberof(keywords); ++i) {
if (values[i] != Qundef) {
rb_ivar_set(self, keywords[i], values[i]);
}
}
{
const rb_execution_context_t *ec = GET_EC();
rb_control_frame_t *cfp =
rb_vm_get_ruby_level_next_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(ec->cfp));
if (cfp) iseqw = rb_iseqw_new(cfp->iseq);
}
rb_ivar_set(self, id_iseq, iseqw);
return self;
}
/*
* call-seq:
* name_error.name -> string or nil
*
* Return the name associated with this NameError exception.
*/
static VALUE
name_err_name(VALUE self)
{
return rb_attr_get(self, id_name);
}
/*
* call-seq:
* name_error.local_variables -> array
*
* Return a list of the local variable names defined where this
* NameError exception was raised.
*
* Internal use only.
*/
static VALUE
name_err_local_variables(VALUE self)
{
VALUE vars = rb_attr_get(self, id_local_variables);
if (NIL_P(vars)) {
VALUE iseqw = rb_attr_get(self, id_iseq);
if (!NIL_P(iseqw)) vars = rb_iseqw_local_variables(iseqw);
if (NIL_P(vars)) vars = rb_ary_new();
rb_ivar_set(self, id_local_variables, vars);
}
return vars;
}
static VALUE nometh_err_initialize_options(int argc, VALUE *argv, VALUE self, VALUE options);
/*
* call-seq:
* NoMethodError.new([msg, *, name [, args [, priv]]]) -> no_method_error
*
* Construct a NoMethodError exception for a method of the given name
* called with the given arguments. The name may be accessed using
* the <code>#name</code> method on the resulting object, and the
* arguments using the <code>#args</code> method.
*/
static VALUE
nometh_err_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE options;
argc = rb_scan_args(argc, argv, "*:", NULL, &options);
return nometh_err_initialize_options(argc, argv, self, options);
}
static VALUE
nometh_err_initialize_options(int argc, VALUE *argv, VALUE self, VALUE options)
{
VALUE priv = (argc > 3) && (--argc, RTEST(argv[argc])) ? Qtrue : Qfalse;
VALUE args = (argc > 2) ? argv[--argc] : Qnil;
name_err_initialize_options(argc, argv, self, options);
rb_ivar_set(self, id_args, args);
rb_ivar_set(self, id_private_call_p, RTEST(priv) ? Qtrue : Qfalse);
return self;
}
/* :nodoc: */
enum {
NAME_ERR_MESG__MESG,
NAME_ERR_MESG__RECV,
NAME_ERR_MESG__NAME,
NAME_ERR_MESG_COUNT
};
static void
name_err_mesg_mark(void *p)
{
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);
}
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,
},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
};
/* :nodoc: */
VALUE
rb_name_err_mesg_new(VALUE mesg, VALUE recv, VALUE method)
{
VALUE result = TypedData_Wrap_Struct(rb_cNameErrorMesg, &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;
return result;
}
VALUE
rb_name_err_new(VALUE mesg, VALUE recv, VALUE method)
{
VALUE exc = rb_obj_alloc(rb_eNameError);
rb_ivar_set(exc, id_mesg, rb_name_err_mesg_new(mesg, recv, method));
rb_ivar_set(exc, id_bt, Qnil);
rb_ivar_set(exc, id_name, method);
rb_ivar_set(exc, id_receiver, recv);
return exc;
}
/* :nodoc: */
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;
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;
}
return Qtrue;
}
/* :nodoc: */
static VALUE
name_err_mesg_to_str(VALUE obj)
{
VALUE *ptr, mesg;
TypedData_Get_Struct(obj, VALUE, &name_err_mesg_data_type, ptr);
mesg = ptr[NAME_ERR_MESG__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();
#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(rb_inspect, obj, &state);
if (state)
rb_set_errinfo(Qnil);
if (NIL_P(d) || RSTRING_LEN(d) > 65) {
d = rb_any_to_s(obj);
}
singleton = (RSTRING_LEN(d) > 0 && RSTRING_PTR(d)[0] == '#');
d = QUOTE(d);
break;
}
if (!singleton) {
s = FAKE_CSTR(&s_str, ":");
c = rb_class_name(CLASS_OF(obj));
}
else {
c = s = FAKE_CSTR(&s_str, "");
}
args[0] = QUOTE(rb_obj_as_string(ptr[NAME_ERR_MESG__NAME]));
args[1] = d;
args[2] = s;
args[3] = c;
mesg = rb_str_format(4, args, mesg);
}
return mesg;
}
/* :nodoc: */
static VALUE
name_err_mesg_dump(VALUE obj, VALUE limit)
{
return name_err_mesg_to_str(obj);
}
/* :nodoc: */
static VALUE
name_err_mesg_load(VALUE klass, VALUE str)
{
return str;
}
/*
* call-seq:
* name_error.receiver -> object
*
* Return the receiver associated with this NameError exception.
*/
static VALUE
name_err_receiver(VALUE self)
{
VALUE *ptr, recv, mesg;
recv = rb_ivar_lookup(self, id_receiver, Qundef);
if (recv != Qundef) return recv;
mesg = rb_attr_get(self, id_mesg);
if (!rb_typeddata_is_kind_of(mesg, &name_err_mesg_data_type)) {
rb_raise(rb_eArgError, "no receiver is available");
}
ptr = DATA_PTR(mesg);
return ptr[NAME_ERR_MESG__RECV];
}
/*
* call-seq:
* no_method_error.args -> obj
*
* Return the arguments passed in as the third parameter to
* the constructor.
*/
static VALUE
nometh_err_args(VALUE self)
{
return rb_attr_get(self, id_args);
}
static VALUE
nometh_err_private_call_p(VALUE self)
{
return rb_attr_get(self, id_private_call_p);
}
void
rb_invalid_str(const char *str, const char *type)
{
VALUE s = rb_str_new2(str);
rb_raise(rb_eArgError, "invalid value for %s: %+"PRIsVALUE, type, s);
}
/*
* call-seq:
* key_error.receiver -> object
*
* Return the receiver associated with this KeyError exception.
*/
static VALUE
key_err_receiver(VALUE self)
{
VALUE recv;
recv = rb_ivar_lookup(self, id_receiver, Qundef);
if (recv != Qundef) return recv;
rb_raise(rb_eArgError, "no receiver is available");
}
/*
* call-seq:
* key_error.key -> object
*
* Return the key caused this KeyError exception.
*/
static VALUE
key_err_key(VALUE self)
{
VALUE key;
key = rb_ivar_lookup(self, id_key, Qundef);
if (key != Qundef) return key;
rb_raise(rb_eArgError, "no key is available");
}
VALUE
rb_key_err_new(VALUE mesg, VALUE recv, VALUE key)
{
VALUE exc = rb_obj_alloc(rb_eKeyError);
rb_ivar_set(exc, id_mesg, mesg);
rb_ivar_set(exc, id_bt, Qnil);
rb_ivar_set(exc, id_key, key);
rb_ivar_set(exc, id_receiver, recv);
return exc;
}
/*
* call-seq:
* KeyError.new(message=nil, receiver: nil, key: nil) -> key_error
*
* Construct a new +KeyError+ exception with the given message,
* receiver and key.
*/
static VALUE
key_err_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE options;
rb_call_super(rb_scan_args(argc, argv, "01:", NULL, &options), argv);
if (!NIL_P(options)) {
ID keywords[2];
VALUE values[numberof(keywords)];
int i;
keywords[0] = id_receiver;
keywords[1] = id_key;
rb_get_kwargs(options, keywords, 0, numberof(values), values);
for (i = 0; i < numberof(values); ++i) {
if (values[i] != Qundef) {
rb_ivar_set(self, keywords[i], values[i]);
}
}
}
return self;
}
/*
* call-seq:
* SyntaxError.new([msg]) -> syntax_error
*
* Construct a SyntaxError exception.
*/
static VALUE
syntax_error_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE mesg;
if (argc == 0) {
mesg = rb_fstring_cstr("compile error");
argc = 1;
argv = &mesg;
}
return rb_call_super(argc, argv);
}
/*
* Document-module: Errno
*
* Ruby exception objects are subclasses of <code>Exception</code>.
* However, operating systems typically report errors using plain
* integers. Module <code>Errno</code> is created dynamically to map
* these operating system errors to Ruby classes, with each error
* number generating its own subclass of <code>SystemCallError</code>.
* As the subclass is created in module <code>Errno</code>, its name
* will start <code>Errno::</code>.
*
* The names of the <code>Errno::</code> classes depend on
* the environment in which Ruby runs. On a typical Unix or Windows
* platform, there are <code>Errno</code> classes such as
* <code>Errno::EACCES</code>, <code>Errno::EAGAIN</code>,
* <code>Errno::EINTR</code>, and so on.
*
* The 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>.
*
* Errno::EACCES::Errno #=> 13
* Errno::EAGAIN::Errno #=> 11
* Errno::EINTR::Errno #=> 4
*
* The full list of operating system errors on your particular platform
* are available as the constants of <code>Errno</code>.
*
* Errno.constants #=> :E2BIG, :EACCES, :EADDRINUSE, :EADDRNOTAVAIL, ...
*/
static st_table *syserr_tbl;
static VALUE
set_syserr(int n, const char *name)
{
st_data_t error;
if (!st_lookup(syserr_tbl, n, &error)) {
error = rb_define_class_under(rb_mErrno, name, rb_eSystemCallError);
/* capture nonblock errnos for WaitReadable/WaitWritable subclasses */
switch (n) {
case EAGAIN:
rb_eEAGAIN = error;
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
break;
case EWOULDBLOCK:
#endif
rb_eEWOULDBLOCK = error;
break;
case EINPROGRESS:
rb_eEINPROGRESS = error;
break;
}
rb_define_const(error, "Errno", INT2NUM(n));
st_add_direct(syserr_tbl, n, error);
}
else {
rb_define_const(rb_mErrno, name, error);
}
return error;
}
static VALUE
get_syserr(int n)
{
st_data_t error;
if (!st_lookup(syserr_tbl, n, &error)) {
char name[8]; /* some Windows' errno have 5 digits. */
snprintf(name, sizeof(name), "E%03d", n);
error = set_syserr(n, name);
}
return error;
}
/*
* call-seq:
* SystemCallError.new(msg, errno) -> system_call_error_subclass
*
* If _errno_ corresponds to a known system error code, constructs
* the appropriate <code>Errno</code> class for that error, otherwise
* constructs a generic <code>SystemCallError</code> object. The
* error number is subsequently available via the <code>errno</code>
* method.
*/
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);
}
}
else {
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);
if (!NIL_P(func)) rb_str_catf(errmsg, " @ %"PRIsVALUE, func);
rb_str_catf(errmsg, " - %"PRIsVALUE, str);
OBJ_INFECT(errmsg, mesg);
}
mesg = errmsg;
rb_call_super(1, &mesg);
rb_ivar_set(self, id_errno, error);
return self;
}
/*
* call-seq:
* system_call_error.errno -> integer
*
* Return this SystemCallError's error number.
*/
static VALUE
syserr_errno(VALUE self)
{
return rb_attr_get(self, id_errno);
}
/*
* call-seq:
* system_call_error === other -> true or false
*
* Return +true+ if the receiver is a generic +SystemCallError+, or
* if the error numbers +self+ and _other_ are the same.
*/
static VALUE
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;
}
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);
}
e = rb_const_get(self, id_Errno);
if (FIXNUM_P(num) ? num == e : rb_equal(num, e))
return Qtrue;
return Qfalse;
}
/*
* Document-class: StandardError
*
* The most standard error types are subclasses of StandardError. A
* rescue clause without an explicit Exception class will rescue all
* StandardErrors (and only those).
*
* def foo
* raise "Oups"
* end
* foo rescue "Hello" #=> "Hello"
*
* On the other hand:
*
* require 'does/not/exist' rescue "Hi"
*
* <em>raises the exception:</em>
*
* LoadError: no such file to load -- does/not/exist
*
*/
/*
* Document-class: SystemExit
*
* Raised by +exit+ to initiate the termination of the script.
*/
/*
* Document-class: SignalException
*
* Raised when a signal is received.
*
* begin
* Process.kill('HUP',Process.pid)
* sleep # wait for receiver to handle signal sent by Process.kill
* rescue SignalException => e
* puts "received Exception #{e}"
* end
*
* <em>produces:</em>
*
* received Exception SIGHUP
*/
/*
* Document-class: Interrupt
*
* Raised with the interrupt signal is received, typically because the
* user pressed on Control-C (on most posix platforms). As such, it is a
* subclass of +SignalException+.
*
* begin
* puts "Press ctrl-C when you get bored"
* loop {}
* rescue Interrupt => e
* puts "Note: You will typically use Signal.trap instead."
* end
*
* <em>produces:</em>
*
* Press ctrl-C when you get bored
*
* <em>then waits until it is interrupted with Control-C and then prints:</em>
*
* Note: You will typically use Signal.trap instead.
*/
/*
* Document-class: TypeError
*
* Raised when encountering an object that is not of the expected type.
*
* [1, 2, 3].first("two")
*
* <em>raises the exception:</em>
*
* TypeError: no implicit conversion of String into Integer
*
*/
/*
* Document-class: ArgumentError
*
* Raised when the arguments are wrong and there isn't a more specific
* Exception class.
*
* Ex: passing the wrong number of arguments
*
* [1, 2, 3].first(4, 5)
*
* <em>raises the exception:</em>
*
* ArgumentError: wrong number of arguments (given 2, expected 1)
*
* Ex: passing an argument that is not acceptable:
*
* [1, 2, 3].first(-4)
*
* <em>raises the exception:</em>
*
* ArgumentError: negative array size
*/
/*
* Document-class: IndexError
*
* Raised when the given index is invalid.
*
* a = [:foo, :bar]
* a.fetch(0) #=> :foo
* a[4] #=> nil
* a.fetch(4) #=> IndexError: index 4 outside of array bounds: -2...2
*
*/
/*
* Document-class: KeyError
*
* Raised when the specified key is not found. It is a subclass of
* IndexError.
*
* h = {"foo" => :bar}
* h.fetch("foo") #=> :bar
* h.fetch("baz") #=> KeyError: key not found: "baz"
*
*/
/*
* Document-class: RangeError
*
* Raised when a given numerical value is out of range.
*
* [1, 2, 3].drop(1 << 100)
*
* <em>raises the exception:</em>
*
* RangeError: bignum too big to convert into `long'
*/
/*
* Document-class: ScriptError
*
* ScriptError is the superclass for errors raised when a script
* can not be executed because of a +LoadError+,
* +NotImplementedError+ or a +SyntaxError+. Note these type of
* +ScriptErrors+ are not +StandardError+ and will not be
* rescued unless it is specified explicitly (or its ancestor
* +Exception+).
*/
/*
* Document-class: SyntaxError
*
* Raised when encountering Ruby code with an invalid syntax.
*
* eval("1+1=2")
*
* <em>raises the exception:</em>
*
* SyntaxError: (eval):1: syntax error, unexpected '=', expecting $end
*/
/*
* Document-class: LoadError
*
* Raised when a file required (a Ruby script, extension library, ...)
* fails to load.
*
* require 'this/file/does/not/exist'
*
* <em>raises the exception:</em>
*
* LoadError: no such file to load -- this/file/does/not/exist
*/
/*
* Document-class: NotImplementedError
*
* Raised when a feature is not implemented on the current platform. For
* example, methods depending on the +fsync+ or +fork+ system calls may
* raise this exception if the underlying operating system or Ruby
* runtime does not support them.
*
* Note that if +fork+ raises a +NotImplementedError+, then
* <code>respond_to?(:fork)</code> returns +false+.
*/
/*
* Document-class: NameError
*
* Raised when a given name is invalid or undefined.
*
* puts foo
*
* <em>raises the exception:</em>
*
* NameError: undefined local variable or method `foo' for main:Object
*
* Since constant names must start with a capital:
*
* Integer.const_set :answer, 42
*
* <em>raises the exception:</em>
*
* NameError: wrong constant name answer
*/
/*
* Document-class: NoMethodError
*
* Raised when a method is called on a receiver which doesn't have it
* defined and also fails to respond with +method_missing+.
*
* "hello".to_ary
*
* <em>raises the exception:</em>
*
* NoMethodError: undefined method `to_ary' for "hello":String
*/
/*
* Document-class: FrozenError
*
* Raised when there is an attempt to modify a frozen object.
*
* [1, 2, 3].freeze << 4
*
* <em>raises the exception:</em>
*
* FrozenError: can't modify frozen Array
*/
/*
* Document-class: RuntimeError
*
* A generic error class raised when an invalid operation is attempted.
* Kernel#raise will raise a RuntimeError if no Exception class is
* specified.
*
* raise "ouch"
*
* <em>raises the exception:</em>
*
* RuntimeError: ouch
*/
/*
* Document-class: SecurityError
*
* Raised when attempting a potential unsafe operation, typically when
* the $SAFE level is raised above 0.
*
* foo = "bar"
* proc = Proc.new do
* $SAFE = 3
* foo.untaint
* end
* proc.call
*
* <em>raises the exception:</em>
*
* SecurityError: Insecure: Insecure operation `untaint' at level 3
*/
/*
* Document-class: NoMemoryError
*
* Raised when memory allocation fails.
*/
/*
* Document-class: SystemCallError
*
* SystemCallError is the base class for all low-level
* platform-dependent errors.
*
* The errors available on the current platform are subclasses of
* SystemCallError and are defined in the Errno module.
*
* File.open("does/not/exist")
*
* <em>raises the exception:</em>
*
* Errno::ENOENT: No such file or directory - does/not/exist
*/
/*
* Document-class: EncodingError
*
* EncodingError is the base class for encoding errors.
*/
/*
* Document-class: Encoding::CompatibilityError
*
* Raised by Encoding and String methods when the source encoding is
* incompatible with the target encoding.
*/
/*
* Document-class: fatal
*
* fatal is an Exception that is raised when Ruby has encountered a fatal
* error and must exit. You are not able to rescue fatal.
*/
/*
* Document-class: NameError::message
* :nodoc:
*/
/*
* Descendants of class Exception are used to communicate between
* Kernel#raise and +rescue+ statements in <code>begin ... end</code> blocks.
* Exception objects carry information about the exception -- its type (the
* exception's class name), an optional descriptive string, and optional
* traceback information. Exception subclasses may add additional
* information like NameError#name.
*
* Programs may make subclasses of Exception, typically of StandardError or
* RuntimeError, to provide custom classes and add additional information.
* See the subclass list below for defaults for +raise+ and +rescue+.
*
* When an exception has been raised but not yet handled (in +rescue+,
* +ensure+, +at_exit+ and +END+ blocks) the global variable <code>$!</code>
* will contain the current exception and <code>$@</code> contains the
* current exception's backtrace.
*
* It is recommended that a library should have one subclass of StandardError
* or RuntimeError and have specific exception types inherit from it. This
* allows the user to rescue a generic exception type to catch all exceptions
* the library may raise even if future versions of the library add new
* exception subclasses.
*
* For example:
*
* class MyLibrary
* class Error < RuntimeError
* end
*
* class WidgetError < Error
* end
*
* class FrobError < Error
* end
*
* end
*
* To handle both WidgetError and FrobError the library user can rescue
* MyLibrary::Error.
*
* The built-in subclasses of Exception are:
*
* * NoMemoryError
* * ScriptError
* * LoadError
* * NotImplementedError
* * SyntaxError
* * SecurityError
* * SignalException
* * Interrupt
* * StandardError -- default for +rescue+
* * ArgumentError
* * UncaughtThrowError
* * EncodingError
* * FiberError
* * IOError
* * EOFError
* * IndexError
* * KeyError
* * StopIteration
* * LocalJumpError
* * NameError
* * NoMethodError
* * RangeError
* * FloatDomainError
* * RegexpError
* * RuntimeError -- default for +raise+
* * SystemCallError
* * Errno::*
* * ThreadError
* * TypeError
* * ZeroDivisionError
* * SystemExit
* * SystemStackError
* * fatal -- impossible to rescue
*/
void
Init_Exception(void)
{
rb_eException = rb_define_class("Exception", rb_cObject);
rb_define_singleton_method(rb_eException, "exception", rb_class_new_instance, -1);
rb_define_method(rb_eException, "exception", exc_exception, -1);
rb_define_method(rb_eException, "initialize", exc_initialize, -1);
rb_define_method(rb_eException, "==", exc_equal, 1);
rb_define_method(rb_eException, "to_s", exc_to_s, 0);
rb_define_method(rb_eException, "message", exc_message, 0);
rb_define_method(rb_eException, "full_message", exc_full_message, 0);
rb_define_method(rb_eException, "inspect", exc_inspect, 0);
rb_define_method(rb_eException, "backtrace", exc_backtrace, 0);
rb_define_method(rb_eException, "backtrace_locations", exc_backtrace_locations, 0);
rb_define_method(rb_eException, "set_backtrace", exc_set_backtrace, 1);
rb_define_method(rb_eException, "cause", exc_cause, 0);
rb_eSystemExit = rb_define_class("SystemExit", rb_eException);
rb_define_method(rb_eSystemExit, "initialize", exit_initialize, -1);
rb_define_method(rb_eSystemExit, "status", exit_status, 0);
rb_define_method(rb_eSystemExit, "success?", exit_success_p, 0);
rb_eFatal = rb_define_class("fatal", rb_eException);
rb_eSignal = rb_define_class("SignalException", rb_eException);
rb_eInterrupt = rb_define_class("Interrupt", rb_eSignal);
rb_eStandardError = rb_define_class("StandardError", rb_eException);
rb_eTypeError = rb_define_class("TypeError", rb_eStandardError);
rb_eArgError = rb_define_class("ArgumentError", rb_eStandardError);
rb_eIndexError = rb_define_class("IndexError", rb_eStandardError);
rb_eKeyError = rb_define_class("KeyError", rb_eIndexError);
rb_define_method(rb_eKeyError, "initialize", key_err_initialize, -1);
rb_define_method(rb_eKeyError, "receiver", key_err_receiver, 0);
rb_define_method(rb_eKeyError, "key", key_err_key, 0);
rb_eRangeError = rb_define_class("RangeError", rb_eStandardError);
rb_eScriptError = rb_define_class("ScriptError", rb_eException);
rb_eSyntaxError = rb_define_class("SyntaxError", rb_eScriptError);
rb_define_method(rb_eSyntaxError, "initialize", syntax_error_initialize, -1);
rb_eLoadError = rb_define_class("LoadError", rb_eScriptError);
/* the path failed to load */
rb_attr(rb_eLoadError, rb_intern_const("path"), 1, 0, Qfalse);
rb_eNotImpError = rb_define_class("NotImplementedError", rb_eScriptError);
rb_eNameError = rb_define_class("NameError", rb_eStandardError);
rb_define_method(rb_eNameError, "initialize", name_err_initialize, -1);
rb_define_method(rb_eNameError, "name", name_err_name, 0);
rb_define_method(rb_eNameError, "receiver", name_err_receiver, 0);
rb_define_method(rb_eNameError, "local_variables", name_err_local_variables, 0);
rb_cNameErrorMesg = rb_define_class_under(rb_eNameError, "message", rb_cData);
rb_define_method(rb_cNameErrorMesg, "==", name_err_mesg_equal, 1);
rb_define_method(rb_cNameErrorMesg, "to_str", name_err_mesg_to_str, 0);
rb_define_method(rb_cNameErrorMesg, "_dump", name_err_mesg_dump, 1);
rb_define_singleton_method(rb_cNameErrorMesg, "_load", name_err_mesg_load, 1);
rb_eNoMethodError = rb_define_class("NoMethodError", rb_eNameError);
rb_define_method(rb_eNoMethodError, "initialize", nometh_err_initialize, -1);
rb_define_method(rb_eNoMethodError, "args", nometh_err_args, 0);
rb_define_method(rb_eNoMethodError, "private_call?", nometh_err_private_call_p, 0);
rb_eRuntimeError = rb_define_class("RuntimeError", rb_eStandardError);
rb_eFrozenError = rb_define_class("FrozenError", rb_eRuntimeError);
rb_eSecurityError = rb_define_class("SecurityError", rb_eException);
rb_eNoMemError = rb_define_class("NoMemoryError", rb_eException);
rb_eEncodingError = rb_define_class("EncodingError", rb_eStandardError);
rb_eEncCompatError = rb_define_class_under(rb_cEncoding, "CompatibilityError", rb_eEncodingError);
syserr_tbl = st_init_numtable();
rb_eSystemCallError = rb_define_class("SystemCallError", rb_eStandardError);
rb_define_method(rb_eSystemCallError, "initialize", syserr_initialize, -1);
rb_define_method(rb_eSystemCallError, "errno", syserr_errno, 0);
rb_define_singleton_method(rb_eSystemCallError, "===", syserr_eqq, 1);
rb_mErrno = rb_define_module("Errno");
rb_mWarning = rb_define_module("Warning");
rb_define_method(rb_mWarning, "warn", rb_warning_s_warn, 1);
rb_extend_object(rb_mWarning, rb_mWarning);
rb_cWarningBuffer = rb_define_class_under(rb_mWarning, "buffer", rb_cString);
rb_define_method(rb_cWarningBuffer, "write", warning_write, -1);
rb_define_global_function("warn", rb_warn_m, -1);
id_new = rb_intern_const("new");
id_cause = rb_intern_const("cause");
id_message = rb_intern_const("message");
id_backtrace = rb_intern_const("backtrace");
id_name = rb_intern_const("name");
id_key = rb_intern_const("key");
id_args = rb_intern_const("args");
id_receiver = rb_intern_const("receiver");
id_private_call_p = rb_intern_const("private_call?");
id_local_variables = rb_intern_const("local_variables");
id_Errno = rb_intern_const("Errno");
id_errno = rb_intern_const("errno");
id_i_path = rb_intern_const("@path");
id_warn = rb_intern_const("warn");
id_iseq = rb_make_internal_id();
}
void
rb_enc_raise(rb_encoding *enc, VALUE exc, const char *fmt, ...)
{
va_list args;
VALUE mesg;
va_start(args, fmt);
mesg = rb_enc_vsprintf(enc, fmt, args);
va_end(args);
rb_exc_raise(rb_exc_new3(exc, mesg));
}
void
rb_raise(VALUE exc, const char *fmt, ...)
{
va_list args;
VALUE mesg;
va_start(args, fmt);
mesg = rb_vsprintf(fmt, args);
va_end(args);
rb_exc_raise(rb_exc_new3(exc, mesg));
}
NORETURN(static void raise_loaderror(VALUE path, VALUE mesg));
static void
raise_loaderror(VALUE path, VALUE mesg)
{
VALUE err = rb_exc_new3(rb_eLoadError, mesg);
rb_ivar_set(err, id_i_path, path);
rb_exc_raise(err);
}
void
rb_loaderror(const char *fmt, ...)
{
va_list args;
VALUE mesg;
va_start(args, fmt);
mesg = rb_enc_vsprintf(rb_locale_encoding(), fmt, args);
va_end(args);
raise_loaderror(Qnil, mesg);
}
void
rb_loaderror_with_path(VALUE path, const char *fmt, ...)
{
va_list args;
VALUE mesg;
va_start(args, fmt);
mesg = rb_enc_vsprintf(rb_locale_encoding(), fmt, args);
va_end(args);
raise_loaderror(path, mesg);
}
void
rb_notimplement(void)
{
rb_raise(rb_eNotImpError,
"%"PRIsVALUE"() function is unimplemented on this machine",
rb_id2str(rb_frame_this_func()));
}
void
rb_fatal(const char *fmt, ...)
{
va_list args;
VALUE mesg;
va_start(args, fmt);
mesg = rb_vsprintf(fmt, args);
va_end(args);
rb_exc_fatal(rb_exc_new3(rb_eFatal, mesg));
}
static VALUE
make_errno_exc(const char *mesg)
{
int n = errno;
errno = 0;
if (n == 0) {
rb_bug("rb_sys_fail(%s) - errno == 0", mesg ? mesg : "");
}
return rb_syserr_new(n, mesg);
}
static VALUE
make_errno_exc_str(VALUE mesg)
{
int n = errno;
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);
}
return rb_syserr_new_str(n, mesg);
}
VALUE
rb_syserr_new(int n, const char *mesg)
{
VALUE arg;
arg = mesg ? rb_str_new2(mesg) : Qnil;
return rb_syserr_new_str(n, arg);
}
VALUE
rb_syserr_new_str(int n, VALUE arg)
{
return rb_class_new_instance(1, &arg, get_syserr(n));
}
void
rb_syserr_fail(int e, const char *mesg)
{
rb_exc_raise(rb_syserr_new(e, mesg));
}
void
rb_syserr_fail_str(int e, VALUE mesg)
{
rb_exc_raise(rb_syserr_new_str(e, mesg));
}
void
rb_sys_fail(const char *mesg)
{
rb_exc_raise(make_errno_exc(mesg));
}
void
rb_sys_fail_str(VALUE mesg)
{
rb_exc_raise(make_errno_exc_str(mesg));
}
#ifdef RUBY_FUNCTION_NAME_STRING
void
rb_sys_fail_path_in(const char *func_name, VALUE path)
{
int n = errno;
errno = 0;
rb_syserr_fail_path_in(func_name, n, path);
}
void
rb_syserr_fail_path_in(const char *func_name, int n, VALUE path)
{
VALUE args[2];
if (!path) path = Qnil;
if (n == 0) {
const char *s = !NIL_P(path) ? RSTRING_PTR(path) : "";
if (!func_name) func_name = "(null)";
rb_bug("rb_sys_fail_path_in(%s, %s) - errno == 0",
func_name, s);
}
args[0] = path;
args[1] = rb_str_new_cstr(func_name);
rb_exc_raise(rb_class_new_instance(2, args, get_syserr(n)));
}
#endif
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);
}
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);
}
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);
}
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);
}
static void
syserr_warning(VALUE mesg, int err)
{
rb_str_set_len(mesg, RSTRING_LEN(mesg)-1);
rb_str_catf(mesg, ": %s\n", strerror(err));
rb_write_warning_str(mesg);
}
#if 0
void
rb_sys_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;
}
}
void
rb_syserr_warn(int err, const char *fmt, ...)
{
if (!NIL_P(ruby_verbose)) {
with_warning_string(mesg, 0, fmt) {
syserr_warning(mesg, err);
}
}
}
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;
}
}
void
rb_syserr_enc_warn(int err, rb_encoding *enc, const char *fmt, ...)
{
if (!NIL_P(ruby_verbose)) {
with_warning_string(mesg, enc, fmt) {
syserr_warning(mesg, err);
}
}
}
#endif
void
rb_sys_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;
}
}
#if 0
void
rb_syserr_warning(int err, const char *fmt, ...)
{
if (RTEST(ruby_verbose)) {
with_warning_string(mesg, 0, fmt) {
syserr_warning(mesg, err);
}
}
}
#endif
void
rb_sys_enc_warning(rb_encoding *enc, const char *fmt, ...)
{
if (RTEST(ruby_verbose)) {
int errno_save = errno;
with_warning_string(mesg, enc, fmt) {
syserr_warning(mesg, errno_save);
}
errno = errno_save;
}
}
void
rb_syserr_enc_warning(int err, rb_encoding *enc, const char *fmt, ...)
{
if (RTEST(ruby_verbose)) {
with_warning_string(mesg, enc, fmt) {
syserr_warning(mesg, err);
}
}
}
void
rb_load_fail(VALUE path, const char *err)
{
VALUE mesg = rb_str_buf_new_cstr(err);
rb_str_cat2(mesg, " -- ");
rb_str_append(mesg, path); /* should be ASCII compatible */
raise_loaderror(path, mesg);
}
void
rb_error_frozen(const char *what)
{
rb_raise(rb_eFrozenError, "can't modify frozen %s", what);
}
void
rb_error_frozen_object(VALUE frozen_obj)
{
VALUE debug_info;
const ID created_info = id_debug_created_info;
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_raise(rb_eFrozenError, "can't modify frozen %"PRIsVALUE", created at %"PRIsVALUE":%"PRIsVALUE,
CLASS_OF(frozen_obj), path, line);
}
else {
rb_raise(rb_eFrozenError, "can't modify frozen %"PRIsVALUE,
CLASS_OF(frozen_obj));
}
}
#undef rb_check_frozen
void
rb_check_frozen(VALUE obj)
{
rb_check_frozen_internal(obj);
}
void
rb_error_untrusted(VALUE obj)
{
}
#undef rb_check_trusted
void
rb_check_trusted(VALUE obj)
{
}
void
rb_check_copyable(VALUE obj, VALUE orig)
{
if (!FL_ABLE(obj)) return;
rb_check_frozen_internal(obj);
if (!FL_ABLE(orig)) return;
if ((~RBASIC(obj)->flags & RBASIC(orig)->flags) & FL_TAINT) {
if (rb_safe_level() > 0) {
rb_raise(rb_eSecurityError, "Insecure: can't modify %"PRIsVALUE,
RBASIC(obj)->klass);
}
}
}
void
Init_syserr(void)
{
rb_eNOERROR = set_syserr(0, "NOERROR");
#define defined_error(name, num) set_syserr((num), (name));
#define undefined_error(name) set_syserr(0, (name));
#include "known_errors.inc"
#undef defined_error
#undef undefined_error
}
/*!
* \}
*/