Feature #6286 ยป 0001-Feature-6286.patch
eval.c | ||
---|---|---|
Init_vm_eval();
|
||
Init_eval_method();
|
||
Init_eval_error();
|
||
rb_define_singleton_method(rb_cModule, "nesting", rb_mod_nesting, 0);
|
||
rb_define_singleton_method(rb_cModule, "constants", rb_mod_s_constants, -1);
|
eval_error.c | ||
---|---|---|
static void
|
||
error_print(void)
|
||
{
|
||
volatile VALUE errat = Qnil; /* OK */
|
||
rb_thread_t *th = GET_THREAD();
|
||
VALUE errinfo = th->errinfo;
|
||
int raised_flag = th->raised_flag;
|
||
volatile VALUE eclass, e;
|
||
const char *volatile einfo;
|
||
volatile long elen;
|
||
if (NIL_P(errinfo))
|
||
return;
|
||
... | ... | |
PUSH_TAG();
|
||
if (EXEC_TAG() == 0) {
|
||
errat = get_backtrace(errinfo);
|
||
}
|
||
else {
|
||
errat = Qnil;
|
||
VALUE mesgs = rb_funcall(errinfo, rb_intern("format"), 0);
|
||
if (RB_TYPE_P(mesgs, T_ARRAY)) {
|
||
OBJ_FREEZE(mesgs);
|
||
rb_write_error_ary(RARRAY_LEN(mesgs), RARRAY_PTR(mesgs));
|
||
}
|
||
}
|
||
if (EXEC_TAG())
|
||
goto error;
|
||
if (NIL_P(errat)) {
|
||
const char *file = rb_sourcefile();
|
||
POP_TAG();
|
||
rb_thread_raised_set(th, raised_flag);
|
||
}
|
||
static VALUE
|
||
error_error_position(VALUE errinfo, VALUE errat)
|
||
{
|
||
VALUE mesg;
|
||
if (NIL_P(errat) ||
|
||
(errat = rb_check_backtrace(errat), RARRAY_LEN(errat)) == 0 ||
|
||
NIL_P(mesg = RARRAY_PTR(errat)[0])) {
|
||
VALUE file = rb_sourcefilename();
|
||
int line = rb_sourceline();
|
||
if (!file)
|
||
warn_printf("%d", line);
|
||
else if (!line)
|
||
warn_printf("%s", file);
|
||
else
|
||
warn_printf("%s:%d", file, line);
|
||
}
|
||
else if (RARRAY_LEN(errat) == 0) {
|
||
error_pos();
|
||
}
|
||
else {
|
||
VALUE mesg = RARRAY_PTR(errat)[0];
|
||
if (NIL_P(mesg))
|
||
error_pos();
|
||
if (NIL_P(file)) {
|
||
mesg = rb_sprintf("%d", line);
|
||
}
|
||
else {
|
||
warn_print2(RSTRING_PTR(mesg), RSTRING_LEN(mesg));
|
||
mesg = rb_str_dup(file);
|
||
if (line) {
|
||
ID callee = rb_frame_callee();
|
||
rb_str_catf(mesg, ":%d", line);
|
||
if (callee != 0) {
|
||
rb_str_buf_cat_ascii(mesg, ":in `");
|
||
rb_str_append(mesg, rb_id2str(callee));
|
||
rb_str_buf_cat_ascii(mesg, "'");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
eclass = CLASS_OF(errinfo);
|
||
if (EXEC_TAG() == 0) {
|
||
e = rb_funcall(errinfo, rb_intern("message"), 0, 0);
|
||
StringValue(e);
|
||
einfo = RSTRING_PTR(e);
|
||
elen = RSTRING_LEN(e);
|
||
}
|
||
else {
|
||
einfo = "";
|
||
elen = 0;
|
||
mesg = rb_str_dup(mesg);
|
||
}
|
||
if (EXEC_TAG())
|
||
goto error;
|
||
if (eclass == rb_eRuntimeError && elen == 0) {
|
||
warn_print(": unhandled exception\n");
|
||
return mesg;
|
||
}
|
||
static VALUE
|
||
error_append_message(VALUE errinfo, VALUE mesg, VALUE errmsg)
|
||
{
|
||
const char *einfo = StringValueCStr(errmsg);
|
||
long elen = RSTRING_LEN(errmsg);
|
||
VALUE epath = rb_class_name(CLASS_OF(errinfo));
|
||
const char *tail = 0;
|
||
long len = elen;
|
||
StringValue(mesg);
|
||
rb_str_buf_cat_ascii(mesg, ": ");
|
||
if (elen == 0) {
|
||
rb_str_append(mesg, epath);
|
||
rb_str_buf_cat_ascii(mesg, "\n");
|
||
}
|
||
else {
|
||
VALUE epath;
|
||
epath = rb_class_name(eclass);
|
||
if (elen == 0) {
|
||
warn_print(": ");
|
||
warn_print2(RSTRING_PTR(epath), RSTRING_LEN(epath));
|
||
warn_print("\n");
|
||
if (RSTRING_PTR(epath)[0] == '#')
|
||
epath = 0;
|
||
if ((tail = memchr(einfo, '\n', elen)) != 0) {
|
||
len = tail - einfo;
|
||
tail++; /* skip newline */
|
||
}
|
||
else {
|
||
char *tail = 0;
|
||
long len = elen;
|
||
if (RSTRING_PTR(epath)[0] == '#')
|
||
epath = 0;
|
||
if ((tail = memchr(einfo, '\n', elen)) != 0) {
|
||
len = tail - einfo;
|
||
tail++; /* skip newline */
|
||
}
|
||
warn_print(": ");
|
||
warn_print2(einfo, len);
|
||
if (epath) {
|
||
warn_print(" (");
|
||
warn_print2(RSTRING_PTR(epath), RSTRING_LEN(epath));
|
||
warn_print(")\n");
|
||
}
|
||
if (tail) {
|
||
warn_print2(tail, elen - len - 1);
|
||
if (einfo[elen-1] != '\n') warn_print2("\n", 1);
|
||
}
|
||
rb_str_cat(mesg, einfo, len);
|
||
if (epath) {
|
||
rb_str_buf_cat_ascii(mesg, " (");
|
||
rb_str_append(mesg, epath);
|
||
rb_str_buf_cat_ascii(mesg, ")\n");
|
||
}
|
||
if (tail) {
|
||
rb_str_buf_cat(mesg, tail, elen - len - 1);
|
||
}
|
||
if (RSTRING_PTR(mesg)[RSTRING_LEN(mesg)-1] != '\n') {
|
||
rb_str_buf_cat_ascii(mesg, "\n");
|
||
}
|
||
}
|
||
return mesg;
|
||
}
|
||
static VALUE
|
||
runtimeerror_append_message(VALUE errinfo, VALUE mesg, VALUE errmsg)
|
||
{
|
||
StringValue(mesg);
|
||
StringValue(errmsg);
|
||
if (RSTRING_LEN(errmsg) == 0) {
|
||
rb_str_buf_cat_ascii(mesg, ": unhandled exception\n");
|
||
return mesg;
|
||
}
|
||
else {
|
||
VALUE args[2];
|
||
args[0] = mesg;
|
||
args[1] = errmsg;
|
||
return rb_call_super(2, args);
|
||
}
|
||
}
|
||
static VALUE
|
||
error_format_backtrace(VALUE errinfo, VALUE errat)
|
||
{
|
||
VALUE result = Qnil;
|
||
if (!NIL_P(errat)) {
|
||
long i;
|
||
long len = RARRAY_LEN(errat);
|
||
VALUE *ptr = RARRAY_PTR(errat);
|
||
int skip = eclass == rb_eSysStackError;
|
||
#define TRACE_MAX (TRACE_HEAD+TRACE_TAIL+5)
|
||
#define TRACE_HEAD 8
|
||
#define TRACE_TAIL 5
|
||
errat = rb_check_backtrace(errat);
|
||
result = rb_ary_new2(RARRAY_LEN(errat));
|
||
for (i = 1; i < len; i++) {
|
||
if (RB_TYPE_P(ptr[i], T_STRING)) {
|
||
warn_printf("\tfrom %s\n", RSTRING_PTR(ptr[i]));
|
||
}
|
||
if (skip && i == TRACE_HEAD && len > TRACE_MAX) {
|
||
warn_printf("\t ... %ld levels...\n",
|
||
len - TRACE_HEAD - TRACE_TAIL);
|
||
i = len - TRACE_TAIL;
|
||
for (i = 1; i < RARRAY_LEN(errat); i++) {
|
||
VALUE mesg = RARRAY_PTR(errat)[i];
|
||
if (RB_TYPE_P(mesg, T_STRING)) {
|
||
mesg = rb_enc_sprintf(rb_enc_get(mesg), "\tfrom %s\n", RSTRING_PTR(mesg));
|
||
rb_ary_push(result, mesg);
|
||
}
|
||
}
|
||
}
|
||
error:
|
||
POP_TAG();
|
||
rb_thread_raised_set(th, raised_flag);
|
||
return result;
|
||
}
|
||
static VALUE
|
||
error_format(VALUE errinfo)
|
||
{
|
||
VALUE errmsg = rb_funcall(errinfo, rb_intern("message"), 0, 0);
|
||
VALUE errat = get_backtrace(errinfo);
|
||
errmsg = rb_funcall(errinfo, rb_intern("append_message"), 2,
|
||
rb_funcall(errinfo, rb_intern("error_position"), 1, errat),
|
||
errmsg);
|
||
errat = rb_funcall(errinfo, rb_intern("format_backtrace"), 1, errat);
|
||
rb_funcall(errat, rb_intern("unshift"), 1, errmsg);
|
||
return errat;
|
||
}
|
||
void
|
||
... | ... | |
rb_threadptr_reset_raised(th);
|
||
return status;
|
||
}
|
||
static void
|
||
Init_eval_error(void)
|
||
{
|
||
rb_define_private_method(rb_eException, "format", error_format, 0);
|
||
rb_define_private_method(rb_eException, "error_position", error_error_position, 1);
|
||
rb_define_private_method(rb_eException, "append_message", error_append_message, 2);
|
||
rb_define_private_method(rb_eException, "format_backtrace", error_format_backtrace, 1);
|
||
rb_define_private_method(rb_eRuntimeError, "append_message", runtimeerror_append_message, 2);
|
||
}
|
include/ruby/intern.h | ||
---|---|---|
VALUE rb_gets(void);
|
||
void rb_write_error(const char*);
|
||
void rb_write_error2(const char*, long);
|
||
void rb_write_error_ary(long, const VALUE*);
|
||
void rb_close_before_exec(int lowfd, int maxhint, VALUE noclose_fds);
|
||
int rb_pipe(int *pipes);
|
||
int rb_reserved_fd_p(int fd);
|
io.c | ||
---|---|---|
}
|
||
void
|
||
rb_write_error_ary(long mesgc, const VALUE *mesgs)
|
||
{
|
||
long i;
|
||
if (rb_stderr == orig_stderr || RFILE(orig_stderr)->fptr->fd < 0) {
|
||
for (i = 0; i < mesgc; ++i) {
|
||
VALUE mesg = mesgs[i];
|
||
size_t len = RSTRING_LEN(mesg);
|
||
if (fwrite(RSTRING_PTR(mesg), sizeof(char), len, stderr) < len) {
|
||
/* failed to write to stderr, what can we do? */
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
for (i = 0; i < mesgc; ++i) {
|
||
rb_io_write(rb_stderr, mesgs[i]);
|
||
}
|
||
}
|
||
}
|
||
void
|
||
rb_write_error2(const char *mesg, long len)
|
||
{
|
||
if (rb_stderr == orig_stderr || RFILE(orig_stderr)->fptr->fd < 0) {
|
prelude.rb | ||
---|---|---|
}
|
||
end
|
||
end
|
||
class SystemStackError
|
||
TRACE_HEAD = 8
|
||
TRACE_TAIL = 5
|
||
TRACE_MAX = TRACE_HEAD + TRACE_TAIL + 5
|
||
private_constant :TRACE_HEAD, :TRACE_TAIL, :TRACE_MAX
|
||
private
|
||
def format_backtrace(bt)
|
||
if (size = bt.size) > TRACE_MAX
|
||
bt = super(bt[0..TRACE_HEAD] + bt[-TRACE_TAIL..-1])
|
||
bt.insert(8, "\t ... #{size - (TRACE_HEAD - TRACE_TAIL)} levels...\n")
|
||
bt
|
||
else
|
||
super
|
||
end
|
||
end
|
||
end
|
test/ruby/test_exception.rb | ||
---|---|---|
ensure
|
||
t.close(true) if t
|
||
end
|
||
class ErrorPositionTest < RuntimeError
|
||
private
|
||
def error_position(at)
|
||
"FOOBAR"
|
||
end
|
||
end
|
||
Feature6286 = '[ruby-core:44328]'
|
||
Feature6286Pos = [
|
||
"#{__FILE__}:#{__LINE__}".freeze,
|
||
"#{__FILE__}:#{__LINE__}".freeze
|
||
]
|
||
def test_format
|
||
pos = Feature6286Pos
|
||
expected = [
|
||
"#{pos[0]}: foo (RuntimeError)\n",
|
||
"\tfrom #{pos[1]}\n",
|
||
]
|
||
e = assert_raise(RuntimeError) {raise RuntimeError, "foo", pos}
|
||
assert_equal(expected, e.__send__(:format), Feature6286)
|
||
end
|
||
def test_error_position
|
||
pos = Feature6286Pos
|
||
expected = [
|
||
"FOOBAR: foo (TestException::ErrorPositionTest)\n",
|
||
"\tfrom #{pos[1]}\n",
|
||
]
|
||
e = assert_raise(ErrorPositionTest) {raise ErrorPositionTest, "foo", pos}
|
||
assert_equal(expected, e.__send__(:format), Feature6286)
|
||
end
|
||
def test_error_position_anonymous
|
||
pos = Feature6286Pos
|
||
eclass = Class.new(ErrorPositionTest)
|
||
expected = [
|
||
"FOOBAR: foo\n",
|
||
"\tfrom #{pos[1]}\n",
|
||
]
|
||
e = assert_raise(eclass) {raise eclass, "foo", pos}
|
||
assert_equal(expected, e.__send__(:format), Feature6286)
|
||
end
|
||
def test_append_message
|
||
pos = Feature6286Pos
|
||
eclass = Class.new(ErrorPositionTest) do
|
||
private
|
||
def append_message(mesg, err)
|
||
mesg << ": bar\n"
|
||
end
|
||
end
|
||
expected = [
|
||
"FOOBAR: bar\n",
|
||
"\tfrom #{pos[1]}\n",
|
||
]
|
||
e = assert_raise(eclass) {raise eclass, "foo", pos}
|
||
assert_equal(expected, e.__send__(:format), Feature6286)
|
||
end
|
||
def test_format_backtrace
|
||
pos = Feature6286Pos
|
||
eclass = Class.new(ErrorPositionTest) do
|
||
private
|
||
def format_backtrace(at)
|
||
["\tzzz\n"]
|
||
end
|
||
end
|
||
expected = [
|
||
"FOOBAR: foo\n",
|
||
"\tzzz\n",
|
||
]
|
||
e = assert_raise(eclass) {raise eclass, "foo", pos}
|
||
assert_equal(expected, e.__send__(:format), Feature6286)
|
||
end
|
||
end
|