6 #define write_warn(str, x) \
7 (NIL_P(str) ? warn_print(x) : (void)rb_str_cat_cstr(str, x))
8 #define write_warn2(str, x, l) \
9 (NIL_P(str) ? warn_print2(x, l) : (void)rb_str_cat(str, x, l))
10 #define write_warn_enc(str, x, l, enc) \
11 (NIL_P(str) ? warn_print2(x, l) : (void)rb_enc_str_buf_cat(str, x, l, enc))
12 #ifdef HAVE_BUILTIN___BUILTIN_CONSTANT_P
13 #define warn_print(x) RB_GNUC_EXTENSION_BLOCK( \
14 (__builtin_constant_p(x)) ? \
15 rb_write_error2((x), (long)strlen(x)) : \
19 #define warn_print(x) rb_write_error(x)
22 #define warn_print2(x,l) rb_write_error2((x),(l))
24 #define write_warn_str(str,x) NIL_P(str) ? rb_write_error_str(x) : (void)rb_str_concat((str), (x))
25 #define warn_print_str(x) rb_write_error_str(x)
27 static VALUE
error_pos_str(void);
30 error_pos(const VALUE str
)
32 VALUE pos
= error_pos_str();
34 write_warn_str(str
, pos
);
42 VALUE sourcefile
= rb_source_location(&sourceline
);
44 if (!NIL_P(sourcefile
)) {
46 if (sourceline
== 0) {
47 return rb_sprintf("%"PRIsVALUE
": ", sourcefile
);
49 else if ((caller_name
= rb_frame_callee()) != 0) {
50 return rb_sprintf("%"PRIsVALUE
":%d:in '%"PRIsVALUE
"': ",
51 sourcefile
, sourceline
,
52 rb_id2str(caller_name
));
55 return rb_sprintf("%"PRIsVALUE
":%d: ", sourcefile
, sourceline
);
62 set_backtrace(VALUE info
, VALUE bt
)
64 ID set_backtrace
= rb_intern("set_backtrace");
66 if (rb_backtrace_p(bt
)) {
67 if (rb_method_basic_definition_p(CLASS_OF(info
), set_backtrace
)) {
68 rb_exc_set_backtrace(info
, bt
);
72 bt
= rb_backtrace_to_str_ary(bt
);
75 rb_check_funcall(info
, set_backtrace
, 1, &bt
);
78 #define CSI_BEGIN "\033["
81 static const char underline
[] = CSI_BEGIN
"1;4"CSI_SGR
;
82 static const char bold
[] = CSI_BEGIN
"1"CSI_SGR
;
83 static const char reset
[] = CSI_BEGIN
""CSI_SGR
;
86 print_errinfo(const VALUE eclass
, const VALUE errat
, const VALUE emesg
, const VALUE str
, int highlight
)
91 if (NIL_P(errat
) || RARRAY_LEN(errat
) == 0 ||
92 NIL_P(mesg
= RARRAY_AREF(errat
, 0))) {
96 write_warn_str(str
, mesg
);
97 write_warn(str
, ": ");
101 elen
= RSTRING_LEN(emesg
);
104 if (eclass
== rb_eRuntimeError
&& elen
== 0) {
105 if (highlight
) write_warn(str
, underline
);
106 write_warn(str
, "unhandled exception");
107 if (highlight
) write_warn(str
, reset
);
108 write_warn(str
, "\n");
113 epath
= rb_class_name(eclass
);
115 if (highlight
) write_warn(str
, underline
);
116 write_warn_str(str
, epath
);
117 if (highlight
) write_warn(str
, reset
);
118 write_warn(str
, "\n");
121 write_warn_str(str
, emesg
);
122 write_warn(str
, "\n");
128 rb_decorate_message(const VALUE eclass
, VALUE emesg
, int highlight
)
130 const char *einfo
= "";
134 VALUE str
= rb_usascii_str_new_cstr("");
136 if (!NIL_P(emesg
) && rb_enc_asciicompat(eenc
= rb_enc_get(emesg
))) {
137 einfo
= RSTRING_PTR(emesg
);
138 elen
= RSTRING_LEN(emesg
);
143 if (eclass
== rb_eRuntimeError
&& elen
== 0) {
144 if (highlight
) write_warn(str
, underline
);
145 write_warn(str
, "unhandled exception");
146 if (highlight
) write_warn(str
, reset
);
151 epath
= rb_class_name(eclass
);
153 if (highlight
) write_warn(str
, underline
);
154 write_warn_str(str
, epath
);
155 if (highlight
) write_warn(str
, reset
);
158 /* emesg is a String instance */
159 const char *tail
= 0;
161 if (highlight
) write_warn(str
, bold
);
162 if (RSTRING_PTR(epath
)[0] == '#')
164 if ((tail
= memchr(einfo
, '\n', elen
)) != 0) {
165 write_warn_enc(str
, einfo
, tail
- einfo
, eenc
);
166 tail
++; /* skip newline */
169 write_warn_str(str
, emesg
);
172 write_warn(str
, " (");
173 if (highlight
) write_warn(str
, underline
);
174 write_warn_str(str
, epath
);
176 write_warn(str
, reset
);
177 write_warn(str
, bold
);
179 write_warn(str
, ")");
180 if (highlight
) write_warn(str
, reset
);
182 if (tail
&& einfo
+elen
> tail
) {
184 write_warn(str
, "\n");
185 write_warn_enc(str
, tail
, einfo
+elen
-tail
, eenc
);
188 elen
-= tail
- einfo
;
190 write_warn(str
, "\n");
192 tail
= memchr(einfo
, '\n', elen
);
193 if (!tail
|| tail
> einfo
) {
194 write_warn(str
, bold
);
195 write_warn_enc(str
, einfo
, tail
? tail
-einfo
: elen
, eenc
);
196 write_warn(str
, reset
);
201 elen
-= tail
- einfo
;
203 do ++tail
; while (tail
< einfo
+elen
&& *tail
== '\n');
204 write_warn_enc(str
, einfo
, tail
-einfo
, eenc
);
205 elen
-= tail
- einfo
;
219 print_backtrace(const VALUE eclass
, const VALUE errat
, const VALUE str
, int reverse
, long backtrace_limit
)
223 long len
= RARRAY_LEN(errat
);
224 const int threshold
= 1000000000;
225 int width
= (len
<= 1) ? INT_MIN
: ((int)log10((double)(len
> threshold
?
226 ((len
- 1) / threshold
) :
228 (len
< threshold
? 0 : 9) + 1);
230 long skip_start
= -1, skip_len
= 0;
232 // skip for stackoverflow
233 if (eclass
== rb_eSysStackError
) {
236 long trace_max
= trace_head
+ trace_tail
+ 5;
237 if (len
> trace_max
) {
238 skip_start
= trace_head
;
239 skip_len
= len
- trace_max
+ 5;
243 // skip for explicit limit
244 if (backtrace_limit
>= 0 && len
> backtrace_limit
+ 2) {
245 skip_start
= backtrace_limit
+ 1;
246 skip_len
= len
- skip_start
;
249 for (i
= 1; i
< len
; i
++) {
250 if (i
== skip_start
) {
251 write_warn_str(str
, rb_sprintf("\t ... %ld levels...\n", skip_len
));
255 VALUE line
= RARRAY_AREF(errat
, reverse
? len
- i
: i
);
256 if (RB_TYPE_P(line
, T_STRING
)) {
257 VALUE bt
= rb_str_new_cstr("\t");
258 if (reverse
) rb_str_catf(bt
, "%*ld: ", width
, len
- i
);
259 write_warn_str(str
, rb_str_catf(bt
, "from %"PRIsVALUE
"\n", line
));
265 VALUE
rb_get_detailed_message(VALUE exc
, VALUE opt
);
268 shown_cause_p(VALUE cause
, VALUE
*shown_causes
)
270 VALUE shown
= *shown_causes
;
272 *shown_causes
= shown
= rb_obj_hide(rb_ident_hash_new());
274 if (rb_hash_has_key(shown
, cause
)) return TRUE
;
275 rb_hash_aset(shown
, cause
, Qtrue
);
280 show_cause(VALUE errinfo
, VALUE str
, VALUE opt
, VALUE highlight
, VALUE reverse
, long backtrace_limit
, VALUE
*shown_causes
)
282 VALUE cause
= rb_attr_get(errinfo
, id_cause
);
283 if (!NIL_P(cause
) && rb_obj_is_kind_of(cause
, rb_eException
) &&
284 !shown_cause_p(cause
, shown_causes
)) {
285 volatile VALUE eclass
= CLASS_OF(cause
);
286 VALUE errat
= rb_get_backtrace(cause
);
287 VALUE emesg
= rb_get_detailed_message(cause
, opt
);
289 show_cause(cause
, str
, opt
, highlight
, reverse
, backtrace_limit
, shown_causes
);
290 print_backtrace(eclass
, errat
, str
, TRUE
, backtrace_limit
);
291 print_errinfo(eclass
, errat
, emesg
, str
, RTEST(highlight
));
294 print_errinfo(eclass
, errat
, emesg
, str
, RTEST(highlight
));
295 print_backtrace(eclass
, errat
, str
, FALSE
, backtrace_limit
);
296 show_cause(cause
, str
, opt
, highlight
, reverse
, backtrace_limit
, shown_causes
);
302 rb_exc_check_circular_cause(VALUE exc
)
304 VALUE cause
= exc
, shown_causes
= 0;
306 if (shown_cause_p(cause
, &shown_causes
)) {
307 rb_raise(rb_eArgError
, "circular causes");
309 } while (!NIL_P(cause
= rb_attr_get(cause
, id_cause
)));
313 rb_error_write(VALUE errinfo
, VALUE emesg
, VALUE errat
, VALUE str
, VALUE opt
, VALUE highlight
, VALUE reverse
)
315 volatile VALUE eclass
;
316 VALUE shown_causes
= 0;
317 long backtrace_limit
= rb_backtrace_length_limit
;
322 if (UNDEF_P(errat
)) {
325 eclass
= CLASS_OF(errinfo
);
327 static const char traceback
[] = "Traceback "
328 "(most recent call last):\n";
329 const int bold_part
= rb_strlen_lit("Traceback");
330 char buff
[sizeof(traceback
)+sizeof(bold
)+sizeof(reset
)-2], *p
= buff
;
331 const char *msg
= traceback
;
332 long len
= sizeof(traceback
) - 1;
333 if (RTEST(highlight
)) {
334 #define APPEND(s, l) (memcpy(p, s, l), p += (l))
335 APPEND(bold
, sizeof(bold
)-1);
336 APPEND(traceback
, bold_part
);
337 APPEND(reset
, sizeof(reset
)-1);
338 APPEND(traceback
+ bold_part
, sizeof(traceback
)-bold_part
-1);
340 len
= p
- (msg
= buff
);
342 write_warn2(str
, msg
, len
);
343 show_cause(errinfo
, str
, opt
, highlight
, reverse
, backtrace_limit
, &shown_causes
);
344 print_backtrace(eclass
, errat
, str
, TRUE
, backtrace_limit
);
345 print_errinfo(eclass
, errat
, emesg
, str
, RTEST(highlight
));
348 print_errinfo(eclass
, errat
, emesg
, str
, RTEST(highlight
));
349 print_backtrace(eclass
, errat
, str
, FALSE
, backtrace_limit
);
350 show_cause(errinfo
, str
, opt
, highlight
, reverse
, backtrace_limit
, &shown_causes
);
355 rb_ec_error_print_detailed(rb_execution_context_t
*const ec
, const VALUE errinfo
, const VALUE str
, VALUE emesg0
)
357 volatile uint8_t raised_flag
= ec
->raised_flag
;
358 volatile VALUE errat
= Qundef
;
359 volatile bool written
= false;
360 volatile VALUE emesg
= emesg0
;
362 VALUE opt
= rb_hash_new();
363 VALUE highlight
= rb_stderr_tty_p() ? Qtrue
: Qfalse
;
364 rb_hash_aset(opt
, ID2SYM(rb_intern_const("highlight")), highlight
);
368 rb_ec_raised_clear(ec
);
371 if (EC_EXEC_TAG() == TAG_NONE
) {
372 errat
= rb_get_backtrace(errinfo
);
374 if (UNDEF_P(emesg
)) {
376 emesg
= rb_get_detailed_message(errinfo
, opt
);
381 rb_error_write(errinfo
, emesg
, errat
, str
, opt
, highlight
, Qfalse
);
385 ec
->errinfo
= errinfo
;
386 rb_ec_raised_set(ec
, raised_flag
);
390 rb_ec_error_print(rb_execution_context_t
*volatile ec
, volatile VALUE errinfo
)
392 rb_ec_error_print_detailed(ec
, errinfo
, Qnil
, Qundef
);
395 #define undef_mesg_for(v, k) rb_fstring_lit("undefined"v" method '%1$s' for "k" '%2$s'")
396 #define undef_mesg(v) ( \
398 undef_mesg_for(v, "module") : \
399 undef_mesg_for(v, "class"))
402 rb_print_undef(VALUE klass
, ID id
, rb_method_visibility_t visi
)
404 const int is_mod
= RB_TYPE_P(klass
, T_MODULE
);
406 switch (visi
& METHOD_VISI_MASK
) {
407 case METHOD_VISI_UNDEF
:
408 case METHOD_VISI_PUBLIC
: mesg
= undef_mesg(""); break;
409 case METHOD_VISI_PRIVATE
: mesg
= undef_mesg(" private"); break;
410 case METHOD_VISI_PROTECTED
: mesg
= undef_mesg(" protected"); break;
411 default: UNREACHABLE
;
413 rb_name_err_raise_str(mesg
, klass
, ID2SYM(id
));
417 rb_print_undef_str(VALUE klass
, VALUE name
)
419 const int is_mod
= RB_TYPE_P(klass
, T_MODULE
);
420 rb_name_err_raise_str(undef_mesg(""), klass
, name
);
423 #define inaccessible_mesg_for(v, k) rb_fstring_lit("method '%1$s' for "k" '%2$s' is "v)
424 #define inaccessible_mesg(v) ( \
426 inaccessible_mesg_for(v, "module") : \
427 inaccessible_mesg_for(v, "class"))
430 rb_print_inaccessible(VALUE klass
, ID id
, rb_method_visibility_t visi
)
432 const int is_mod
= RB_TYPE_P(klass
, T_MODULE
);
434 switch (visi
& METHOD_VISI_MASK
) {
435 case METHOD_VISI_UNDEF
:
436 case METHOD_VISI_PUBLIC
: mesg
= inaccessible_mesg(""); break;
437 case METHOD_VISI_PRIVATE
: mesg
= inaccessible_mesg("private"); break;
438 case METHOD_VISI_PROTECTED
: mesg
= inaccessible_mesg("protected"); break;
439 default: UNREACHABLE
;
441 rb_name_err_raise_str(mesg
, klass
, ID2SYM(id
));
445 sysexit_status(VALUE err
)
447 VALUE st
= rb_ivar_get(err
, id_status
);
452 EXITING_WITH_MESSAGE
= 1,
453 EXITING_WITH_STATUS
= 2,
454 EXITING_WITH_SIGNAL
= 4
457 exiting_split(VALUE errinfo
, volatile int *exitcode
, volatile int *sigstatus
)
459 int ex
= EXIT_SUCCESS
;
464 if (NIL_P(errinfo
)) return 0;
466 if (THROW_DATA_P(errinfo
)) {
467 int throw_state
= ((const struct vm_throw_data
*)errinfo
)->throw_state
;
468 ex
= throw_state
& VM_THROW_STATE_MASK
;
469 result
|= EXITING_WITH_STATUS
;
471 else if (rb_obj_is_kind_of(errinfo
, rb_eSystemExit
)) {
472 ex
= sysexit_status(errinfo
);
473 result
|= EXITING_WITH_STATUS
;
475 else if (rb_obj_is_kind_of(errinfo
, rb_eSignal
)) {
476 signo
= rb_ivar_get(errinfo
, id_signo
);
477 sig
= FIX2INT(signo
);
478 result
|= EXITING_WITH_SIGNAL
;
479 /* no message when exiting by signal */
480 if (signo
== INT2FIX(SIGSEGV
) || !rb_obj_is_instance_of(errinfo
, rb_eSignal
))
481 /* except for SEGV and subclasses */
482 result
|= EXITING_WITH_MESSAGE
;
484 else if (rb_obj_is_kind_of(errinfo
, rb_eSystemCallError
) &&
485 FIXNUM_P(signo
= rb_attr_get(errinfo
, id_signo
))) {
486 sig
= FIX2INT(signo
);
487 result
|= EXITING_WITH_SIGNAL
;
488 /* no message when exiting by error to be mapped to signal */
492 result
|= EXITING_WITH_STATUS
| EXITING_WITH_MESSAGE
;
495 if (exitcode
&& (result
& EXITING_WITH_STATUS
))
497 if (sigstatus
&& (result
& EXITING_WITH_SIGNAL
))
503 #define unknown_longjmp_status(status) \
504 rb_bug("Unknown longjmp status %d", status)
507 error_handle(rb_execution_context_t
*ec
, VALUE errinfo
, enum ruby_tag_type ex
)
509 int status
= EXIT_FAILURE
;
511 if (rb_ec_set_raised(ec
))
513 switch (ex
& TAG_MASK
) {
515 status
= EXIT_SUCCESS
;
520 warn_print("unexpected return\n");
524 warn_print("unexpected next\n");
528 warn_print("unexpected break\n");
532 warn_print("unexpected redo\n");
536 warn_print("retry outside of rescue clause\n");
541 warn_print("unexpected throw\n");
544 if (!(exiting_split(errinfo
, &status
, NULL
) & EXITING_WITH_MESSAGE
)) {
549 rb_ec_error_print(ec
, errinfo
);
552 unknown_longjmp_status(ex
);
555 rb_ec_reset_raised(ec
);