From f4e673d34cd36dbc53a626da9a81ffa94bc179b1 Mon Sep 17 00:00:00 2001 From: Sorah Fukumori Date: Thu, 30 Nov 2017 19:11:18 +0900 Subject: [PATCH 1/2] Add Exception#formatted to get a formatted string [Feature #14141] --- NEWS | 4 +++ error.c | 18 +++++++++++ eval_error.c | 102 ++++++++++++++++++++++++++++++++++++----------------------- 3 files changed, 84 insertions(+), 40 deletions(-) diff --git a/NEWS b/NEWS index d72f05e..27467d2 100644 --- a/NEWS +++ b/NEWS @@ -31,6 +31,10 @@ with all sufficient information, see the ChangeLog file or Redmine * Now deprecated [Feature #3072] +* Exception + + * Exception#formatted [Feature #14141] + * Dir * Dir.glob provides new optional keyword argument, :base. diff --git a/error.c b/error.c index f009519..72e0793 100644 --- a/error.c +++ b/error.c @@ -922,6 +922,22 @@ exc_to_s(VALUE exc) /* * call-seq: + * exception.formatted -> string + * + * Returns formatted string of exception. + * Format includes backtrace, exception class and exception message. + */ + +static VALUE +exc_formatted(VALUE exc) +{ + VALUE str = rb_str_new2(""); + rb_ec_error_write(exc, Qundef, str); + return str; +} + +/* + * call-seq: * exception.message -> string * * Returns the result of invoking exception.to_s. @@ -2182,6 +2198,8 @@ Init_Exception(void) 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, "formatted", exc_formatted, 0); + rb_define_method(rb_eException, "message", exc_message, 0); rb_define_method(rb_eException, "inspect", exc_inspect, 0); rb_define_method(rb_eException, "backtrace", exc_backtrace, 0); diff --git a/eval_error.c b/eval_error.c index 4c79bf0..1e7c77c 100644 --- a/eval_error.c +++ b/eval_error.c @@ -4,15 +4,28 @@ */ #ifdef HAVE_BUILTIN___BUILTIN_CONSTANT_P +#define write_warn(str, x) RB_GNUC_EXTENSION_BLOCK( \ + NIL_P(str) ? \ + warn_print(x) : ( \ + (__builtin_constant_p(x)) ? \ + rb_str_concat((str), rb_str_new((x), (long)strlen(x))) : \ + rb_str_concat((str), rb_str_new2(x)) \ + ) \ + ) #define warn_print(x) RB_GNUC_EXTENSION_BLOCK( \ (__builtin_constant_p(x)) ? \ rb_write_error2((x), (long)strlen(x)) : \ rb_write_error(x) \ ) #else +#define write_warn(str, x) NIL_P(str) ? rb_write_error((x)) : rb_str_concat((str), rb_str_new2(x)) #define warn_print(x) rb_write_error(x) #endif + +#define write_warn2(str,x,l) NIL_P(str) ? warn_print2(x,l) : rb_str_concat((str), rb_str_new((x),(l))) #define warn_print2(x,l) rb_write_error2((x),(l)) + +#define write_warn_str(str,x) NIL_P(str) ? rb_write_error_str(x) : rb_str_concat((str), (x)) #define warn_print_str(x) rb_write_error_str(x) static VALUE error_pos_str(void); @@ -73,7 +86,7 @@ error_print(rb_execution_context_t *ec) } static void -print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, int colored) +print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, const VALUE str, int colored) { static const char underline[] = "\033[4;1m"; static const char bold[] = "\033[1m"; @@ -88,11 +101,11 @@ print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, int colo error_pos(); } else { - warn_print_str(mesg); - warn_print(": "); + write_warn_str(str, mesg); + write_warn(str, ": "); } - if (colored) warn_print(bold); + if (colored) write_warn(str, bold); if (!NIL_P(emesg)) { einfo = RSTRING_PTR(emesg); @@ -101,17 +114,17 @@ print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, int colo } if (eclass == rb_eRuntimeError && elen == 0) { - if (colored) warn_print(underline); - warn_print("unhandled exception\n"); + if (colored) write_warn(str, underline); + write_warn(str, "unhandled exception\n"); } else { VALUE epath; epath = rb_class_name(eclass); if (elen == 0) { - if (colored) warn_print(underline); - warn_print_str(epath); - warn_print("\n"); + if (colored) write_warn(str, underline); + write_warn_str(str, epath); + write_warn(str, "\n"); } else { const char *tail = 0; @@ -123,26 +136,26 @@ print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, int colo len = tail - einfo; tail++; /* skip newline */ } - warn_print_str(tail ? rb_str_subseq(emesg, 0, len) : emesg); + write_warn_str(str, tail ? rb_str_subseq(emesg, 0, len) : emesg); if (epath) { - warn_print(" ("); - if (colored) warn_print(underline); - warn_print_str(epath); - if (colored) warn_print(reset); - if (colored) warn_print(bold); - warn_print(")\n"); + write_warn(str, " ("); + if (colored) write_warn(str, underline); + write_warn_str(str, epath); + if (colored) write_warn(str, reset); + if (colored) write_warn(str, bold); + write_warn(str, ")\n"); } if (tail) { - warn_print_str(rb_str_subseq(emesg, tail - einfo, elen - len - 1)); + write_warn_str(str, rb_str_subseq(emesg, tail - einfo, elen - len - 1)); } - if (tail ? einfo[elen-1] != '\n' : !epath) warn_print2("\n", 1); + if (tail ? einfo[elen-1] != '\n' : !epath) write_warn2(str, "\n", 1); } } - if (colored) warn_print(reset); + if (colored) write_warn(str, reset); } static void -print_backtrace(const VALUE eclass, const VALUE errat, int reverse) +print_backtrace(const VALUE eclass, const VALUE errat, const VALUE str, int reverse) { if (!NIL_P(errat)) { long i; @@ -163,10 +176,10 @@ print_backtrace(const VALUE eclass, const VALUE errat, int reverse) if (RB_TYPE_P(line, T_STRING)) { VALUE str = rb_str_new_cstr("\t"); if (reverse) rb_str_catf(str, "%*ld: ", width, len - i); - warn_print_str(rb_str_catf(str, "from %"PRIsVALUE"\n", line)); + write_warn_str(str, rb_str_catf(str, "from %"PRIsVALUE"\n", line)); } if (skip && i == TRACE_HEAD && len > TRACE_MAX) { - warn_print_str(rb_sprintf("\t ... %ld levels...\n", + write_warn_str(str, rb_sprintf("\t ... %ld levels...\n", len - TRACE_HEAD - TRACE_TAIL)); i = len - TRACE_TAIL; } @@ -175,26 +188,16 @@ print_backtrace(const VALUE eclass, const VALUE errat, int reverse) } void -rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo) +rb_ec_error_write(volatile VALUE errinfo, volatile VALUE errat, volatile VALUE str) { - volatile VALUE errat = Qundef; - volatile int raised_flag = ec->raised_flag; volatile VALUE eclass = Qundef, emesg = Qundef; if (NIL_P(errinfo)) return; - rb_ec_raised_clear(ec); - EC_PUSH_TAG(ec); - if (EC_EXEC_TAG() == TAG_NONE) { - errat = rb_get_backtrace(errinfo); - } - else if (errat == Qundef) { + if (errat == Qundef) { errat = Qnil; } - else if (eclass == Qundef || emesg != Qundef) { - goto error; - } if ((eclass = CLASS_OF(errinfo)) != Qundef) { VALUE e = rb_check_funcall(errinfo, rb_intern("message"), 0, 0); if (e != Qundef) { @@ -203,15 +206,34 @@ rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo) } } if (rb_stderr_tty_p()) { - warn_print("\033[1mTraceback \033[m(most recent call last):\n"); - print_backtrace(eclass, errat, TRUE); - print_errinfo(eclass, errat, emesg, TRUE); + write_warn(str, "\033[1mTraceback \033[m(most recent call last):\n"); + print_backtrace(eclass, errat, str, TRUE); + print_errinfo(eclass, errat, emesg, str, TRUE); } else { - print_errinfo(eclass, errat, emesg, FALSE); - print_backtrace(eclass, errat, FALSE); + print_errinfo(eclass, errat, emesg, str, FALSE); + print_backtrace(eclass, errat, str, FALSE); } - error: +} + +void +rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo) +{ + volatile int raised_flag = ec->raised_flag; + volatile VALUE errat; + + if (NIL_P(errinfo)) + return; + rb_ec_raised_clear(ec); + + EC_PUSH_TAG(ec); + if (EC_EXEC_TAG() == TAG_NONE) { + errat = rb_get_backtrace(errinfo); + } + + rb_ec_error_write(errinfo, errat, Qnil); + +error: EC_POP_TAG(); ec->errinfo = errinfo; rb_ec_raised_set(ec, raised_flag); -- 2.10.2