Ruby 3.2.2p53 (2023-03-30 revision e51014f9c05aa65cbf203442d37fef7c12390015)
eval_error.c
1/* -*-c-*- */
2/*
3 * included by eval.c
4 */
5
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#ifdef HAVE_BUILTIN___BUILTIN_CONSTANT_P
11#define warn_print(x) RB_GNUC_EXTENSION_BLOCK( \
12 (__builtin_constant_p(x)) ? \
13 rb_write_error2((x), (long)strlen(x)) : \
14 rb_write_error(x) \
15)
16#else
17#define warn_print(x) rb_write_error(x)
18#endif
19
20#define warn_print2(x,l) rb_write_error2((x),(l))
21
22#define write_warn_str(str,x) NIL_P(str) ? rb_write_error_str(x) : (void)rb_str_concat((str), (x))
23#define warn_print_str(x) rb_write_error_str(x)
24
25static VALUE error_pos_str(void);
26
27static void
28error_pos(const VALUE str)
29{
30 VALUE pos = error_pos_str();
31 if (!NIL_P(pos)) {
32 write_warn_str(str, pos);
33 }
34}
35
36static VALUE
37error_pos_str(void)
38{
39 int sourceline;
40 VALUE sourcefile = rb_source_location(&sourceline);
41
42 if (!NIL_P(sourcefile)) {
43 ID caller_name;
44 if (sourceline == 0) {
45 return rb_sprintf("%"PRIsVALUE": ", sourcefile);
46 }
47 else if ((caller_name = rb_frame_callee()) != 0) {
48 return rb_sprintf("%"PRIsVALUE":%d:in `%"PRIsVALUE"': ",
49 sourcefile, sourceline,
50 rb_id2str(caller_name));
51 }
52 else {
53 return rb_sprintf("%"PRIsVALUE":%d: ", sourcefile, sourceline);
54 }
55 }
56 return Qnil;
57}
58
59static void
60set_backtrace(VALUE info, VALUE bt)
61{
62 ID set_backtrace = rb_intern("set_backtrace");
63
64 if (rb_backtrace_p(bt)) {
65 if (rb_method_basic_definition_p(CLASS_OF(info), set_backtrace)) {
66 rb_exc_set_backtrace(info, bt);
67 return;
68 }
69 else {
70 bt = rb_backtrace_to_str_ary(bt);
71 }
72 }
73 rb_check_funcall(info, set_backtrace, 1, &bt);
74}
75
76#define CSI_BEGIN "\033["
77#define CSI_SGR "m"
78
79static const char underline[] = CSI_BEGIN"1;4"CSI_SGR;
80static const char bold[] = CSI_BEGIN"1"CSI_SGR;
81static const char reset[] = CSI_BEGIN""CSI_SGR;
82
83static void
84print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, const VALUE str, int highlight)
85{
86 long elen = 0;
87 VALUE mesg;
88
89 if (NIL_P(errat) || RARRAY_LEN(errat) == 0 ||
90 NIL_P(mesg = RARRAY_AREF(errat, 0))) {
91 error_pos(str);
92 }
93 else {
94 write_warn_str(str, mesg);
95 write_warn(str, ": ");
96 }
97
98 if (!NIL_P(emesg)) {
99 elen = RSTRING_LEN(emesg);
100 }
101
102 if (eclass == rb_eRuntimeError && elen == 0) {
103 if (highlight) write_warn(str, underline);
104 write_warn(str, "unhandled exception");
105 if (highlight) write_warn(str, reset);
106 write_warn2(str, "\n", 1);
107 }
108 else {
109 VALUE epath;
110
111 epath = rb_class_name(eclass);
112 if (elen == 0) {
113 if (highlight) write_warn(str, underline);
114 write_warn_str(str, epath);
115 if (highlight) write_warn(str, reset);
116 write_warn(str, "\n");
117 }
118 else {
119 write_warn_str(str, emesg);
120 write_warn(str, "\n");
121 }
122 }
123}
124
125VALUE
126rb_decorate_message(const VALUE eclass, const VALUE emesg, int highlight)
127{
128 const char *einfo = "";
129 long elen = 0;
130
131 VALUE str = rb_str_new2("");
132
133 if (!NIL_P(emesg)) {
134 einfo = RSTRING_PTR(emesg);
135 elen = RSTRING_LEN(emesg);
136 }
137 if (eclass == rb_eRuntimeError && elen == 0) {
138 if (highlight) write_warn(str, underline);
139 write_warn(str, "unhandled exception");
140 if (highlight) write_warn(str, reset);
141 }
142 else {
143 VALUE epath;
144
145 epath = rb_class_name(eclass);
146 if (elen == 0) {
147 if (highlight) write_warn(str, underline);
148 write_warn_str(str, epath);
149 if (highlight) write_warn(str, reset);
150 }
151 else {
152 /* emesg is a String instance */
153 const char *tail = 0;
154
155 if (highlight) write_warn(str, bold);
156 if (RSTRING_PTR(epath)[0] == '#')
157 epath = 0;
158 if ((tail = memchr(einfo, '\n', elen)) != 0) {
159 write_warn2(str, einfo, tail - einfo);
160 tail++; /* skip newline */
161 }
162 else {
163 write_warn_str(str, emesg);
164 }
165 if (epath) {
166 write_warn(str, " (");
167 if (highlight) write_warn(str, underline);
168 write_warn_str(str, epath);
169 if (highlight) {
170 write_warn(str, reset);
171 write_warn(str, bold);
172 }
173 write_warn2(str, ")", 1);
174 if (highlight) write_warn(str, reset);
175 }
176 if (tail && einfo+elen > tail) {
177 if (!highlight) {
178 write_warn2(str, "\n", 1);
179 write_warn2(str, tail, einfo+elen-tail);
180 }
181 else {
182 elen -= tail - einfo;
183 einfo = tail;
184 write_warn2(str, "\n", 1);
185 while (elen > 0) {
186 tail = memchr(einfo, '\n', elen);
187 if (!tail || tail > einfo) {
188 write_warn(str, bold);
189 write_warn2(str, einfo, tail ? tail-einfo : elen);
190 write_warn(str, reset);
191 if (!tail) {
192 break;
193 }
194 }
195 elen -= tail - einfo;
196 einfo = tail;
197 do ++tail; while (tail < einfo+elen && *tail == '\n');
198 write_warn2(str, einfo, tail-einfo);
199 elen -= tail - einfo;
200 einfo = tail;
201 }
202 }
203 }
204 }
205 }
206
207 return str;
208}
209
210static void
211print_backtrace(const VALUE eclass, const VALUE errat, const VALUE str, int reverse, long backtrace_limit)
212{
213 if (!NIL_P(errat)) {
214 long i;
215 long len = RARRAY_LEN(errat);
216 const int threshold = 1000000000;
217 int width = (len <= 1) ? INT_MIN : ((int)log10((double)(len > threshold ?
218 ((len - 1) / threshold) :
219 len - 1)) +
220 (len < threshold ? 0 : 9) + 1);
221
222 long skip_start = -1, skip_len = 0;
223
224 // skip for stackoverflow
225 if (eclass == rb_eSysStackError) {
226 long trace_head = 9;
227 long trace_tail = 4;
228 long trace_max = trace_head + trace_tail + 5;
229 if (len > trace_max) {
230 skip_start = trace_head;
231 skip_len = len - trace_max + 5;
232 }
233 }
234
235 // skip for explicit limit
236 if (backtrace_limit >= 0 && len > backtrace_limit + 2) {
237 skip_start = backtrace_limit + 1;
238 skip_len = len - skip_start;
239 }
240
241 for (i = 1; i < len; i++) {
242 if (i == skip_start) {
243 write_warn_str(str, rb_sprintf("\t ... %ld levels...\n", skip_len));
244 i += skip_len;
245 if (i >= len) break;
246 }
247 VALUE line = RARRAY_AREF(errat, reverse ? len - i : i);
248 if (RB_TYPE_P(line, T_STRING)) {
249 VALUE bt = rb_str_new_cstr("\t");
250 if (reverse) rb_str_catf(bt, "%*ld: ", width, len - i);
251 write_warn_str(str, rb_str_catf(bt, "from %"PRIsVALUE"\n", line));
252 }
253 }
254 }
255}
256
257VALUE rb_get_detailed_message(VALUE exc, VALUE opt);
258
259static int
260shown_cause_p(VALUE cause, VALUE *shown_causes)
261{
262 VALUE shown = *shown_causes;
263 if (!shown) {
264 *shown_causes = shown = rb_obj_hide(rb_ident_hash_new());
265 }
266 if (rb_hash_has_key(shown, cause)) return TRUE;
267 rb_hash_aset(shown, cause, Qtrue);
268 return FALSE;
269}
270
271static void
272show_cause(VALUE errinfo, VALUE str, VALUE opt, VALUE highlight, VALUE reverse, long backtrace_limit, VALUE *shown_causes)
273{
274 VALUE cause = rb_attr_get(errinfo, id_cause);
275 if (!NIL_P(cause) && rb_obj_is_kind_of(cause, rb_eException) &&
276 !shown_cause_p(cause, shown_causes)) {
277 volatile VALUE eclass = CLASS_OF(cause);
278 VALUE errat = rb_get_backtrace(cause);
279 VALUE emesg = rb_get_detailed_message(cause, opt);
280 if (reverse) {
281 show_cause(cause, str, opt, highlight, reverse, backtrace_limit, shown_causes);
282 print_backtrace(eclass, errat, str, TRUE, backtrace_limit);
283 print_errinfo(eclass, errat, emesg, str, RTEST(highlight));
284 }
285 else {
286 print_errinfo(eclass, errat, emesg, str, RTEST(highlight));
287 print_backtrace(eclass, errat, str, FALSE, backtrace_limit);
288 show_cause(cause, str, opt, highlight, reverse, backtrace_limit, shown_causes);
289 }
290 }
291}
292
293void
294rb_exc_check_circular_cause(VALUE exc)
295{
296 VALUE cause = exc, shown_causes = 0;
297 do {
298 if (shown_cause_p(cause, &shown_causes)) {
299 rb_raise(rb_eArgError, "circular causes");
300 }
301 } while (!NIL_P(cause = rb_attr_get(cause, id_cause)));
302}
303
304void
305rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE opt, VALUE highlight, VALUE reverse)
306{
307 volatile VALUE eclass;
308 VALUE shown_causes = 0;
309 long backtrace_limit = rb_backtrace_length_limit;
310
311 if (NIL_P(errinfo))
312 return;
313
314 if (UNDEF_P(errat)) {
315 errat = Qnil;
316 }
317 eclass = CLASS_OF(errinfo);
318 if (reverse) {
319 static const char traceback[] = "Traceback "
320 "(most recent call last):\n";
321 const int bold_part = rb_strlen_lit("Traceback");
322 char buff[sizeof(traceback)+sizeof(bold)+sizeof(reset)-2], *p = buff;
323 const char *msg = traceback;
324 long len = sizeof(traceback) - 1;
325 if (RTEST(highlight)) {
326#define APPEND(s, l) (memcpy(p, s, l), p += (l))
327 APPEND(bold, sizeof(bold)-1);
328 APPEND(traceback, bold_part);
329 APPEND(reset, sizeof(reset)-1);
330 APPEND(traceback + bold_part, sizeof(traceback)-bold_part-1);
331#undef APPEND
332 len = p - (msg = buff);
333 }
334 write_warn2(str, msg, len);
335 show_cause(errinfo, str, opt, highlight, reverse, backtrace_limit, &shown_causes);
336 print_backtrace(eclass, errat, str, TRUE, backtrace_limit);
337 print_errinfo(eclass, errat, emesg, str, RTEST(highlight));
338 }
339 else {
340 print_errinfo(eclass, errat, emesg, str, RTEST(highlight));
341 print_backtrace(eclass, errat, str, FALSE, backtrace_limit);
342 show_cause(errinfo, str, opt, highlight, reverse, backtrace_limit, &shown_causes);
343 }
344}
345
346static void
347rb_ec_error_print_detailed(rb_execution_context_t *const ec, const VALUE errinfo, const VALUE str, VALUE emesg0)
348{
349 volatile uint8_t raised_flag = ec->raised_flag;
350 volatile VALUE errat = Qundef;
351 volatile bool written = false;
352 volatile VALUE emesg = emesg0;
353
354 VALUE opt = rb_hash_new();
355 VALUE highlight = rb_stderr_tty_p() ? Qtrue : Qfalse;
356 rb_hash_aset(opt, ID2SYM(rb_intern_const("highlight")), highlight);
357
358 if (NIL_P(errinfo))
359 return;
360 rb_ec_raised_clear(ec);
361
362 EC_PUSH_TAG(ec);
363 if (EC_EXEC_TAG() == TAG_NONE) {
364 errat = rb_get_backtrace(errinfo);
365 }
366 if (UNDEF_P(emesg)) {
367 emesg = Qnil;
368 emesg = rb_get_detailed_message(errinfo, opt);
369 }
370
371 if (!written) {
372 written = true;
373 rb_error_write(errinfo, emesg, errat, str, opt, highlight, Qfalse);
374 }
375
376 EC_POP_TAG();
377 ec->errinfo = errinfo;
378 rb_ec_raised_set(ec, raised_flag);
379}
380
381void
382rb_ec_error_print(rb_execution_context_t *volatile ec, volatile VALUE errinfo)
383{
384 rb_ec_error_print_detailed(ec, errinfo, Qnil, Qundef);
385}
386
387#define undef_mesg_for(v, k) rb_fstring_lit("undefined"v" method `%1$s' for "k" `%2$s'")
388#define undef_mesg(v) ( \
389 is_mod ? \
390 undef_mesg_for(v, "module") : \
391 undef_mesg_for(v, "class"))
392
393void
394rb_print_undef(VALUE klass, ID id, rb_method_visibility_t visi)
395{
396 const int is_mod = RB_TYPE_P(klass, T_MODULE);
397 VALUE mesg;
398 switch (visi & METHOD_VISI_MASK) {
399 case METHOD_VISI_UNDEF:
400 case METHOD_VISI_PUBLIC: mesg = undef_mesg(""); break;
401 case METHOD_VISI_PRIVATE: mesg = undef_mesg(" private"); break;
402 case METHOD_VISI_PROTECTED: mesg = undef_mesg(" protected"); break;
403 default: UNREACHABLE;
404 }
405 rb_name_err_raise_str(mesg, klass, ID2SYM(id));
406}
407
408void
409rb_print_undef_str(VALUE klass, VALUE name)
410{
411 const int is_mod = RB_TYPE_P(klass, T_MODULE);
412 rb_name_err_raise_str(undef_mesg(""), klass, name);
413}
414
415#define inaccessible_mesg_for(v, k) rb_fstring_lit("method `%1$s' for "k" `%2$s' is "v)
416#define inaccessible_mesg(v) ( \
417 is_mod ? \
418 inaccessible_mesg_for(v, "module") : \
419 inaccessible_mesg_for(v, "class"))
420
421void
422rb_print_inaccessible(VALUE klass, ID id, rb_method_visibility_t visi)
423{
424 const int is_mod = RB_TYPE_P(klass, T_MODULE);
425 VALUE mesg;
426 switch (visi & METHOD_VISI_MASK) {
427 case METHOD_VISI_UNDEF:
428 case METHOD_VISI_PUBLIC: mesg = inaccessible_mesg(""); break;
429 case METHOD_VISI_PRIVATE: mesg = inaccessible_mesg("private"); break;
430 case METHOD_VISI_PROTECTED: mesg = inaccessible_mesg("protected"); break;
431 default: UNREACHABLE;
432 }
433 rb_name_err_raise_str(mesg, klass, ID2SYM(id));
434}
435
436static int
437sysexit_status(VALUE err)
438{
439 VALUE st = rb_ivar_get(err, id_status);
440 return NUM2INT(st);
441}
442
443enum {
444 EXITING_WITH_MESSAGE = 1,
445 EXITING_WITH_STATUS = 2,
446 EXITING_WITH_SIGNAL = 4
447};
448static int
449exiting_split(VALUE errinfo, volatile int *exitcode, volatile int *sigstatus)
450{
451 int ex = EXIT_SUCCESS;
452 VALUE signo;
453 int sig = 0;
454 int result = 0;
455
456 if (NIL_P(errinfo)) return 0;
457
458 if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
459 ex = sysexit_status(errinfo);
460 result |= EXITING_WITH_STATUS;
461 }
462 else if (rb_obj_is_kind_of(errinfo, rb_eSignal)) {
463 signo = rb_ivar_get(errinfo, id_signo);
464 sig = FIX2INT(signo);
465 result |= EXITING_WITH_SIGNAL;
466 /* no message when exiting by signal */
467 if (signo == INT2FIX(SIGSEGV) || !rb_obj_is_instance_of(errinfo, rb_eSignal))
468 /* except for SEGV and subclasses */
469 result |= EXITING_WITH_MESSAGE;
470 }
471 else if (rb_obj_is_kind_of(errinfo, rb_eSystemCallError) &&
472 FIXNUM_P(signo = rb_attr_get(errinfo, id_signo))) {
473 sig = FIX2INT(signo);
474 result |= EXITING_WITH_SIGNAL;
475 /* no message when exiting by error to be mapped to signal */
476 }
477 else {
478 ex = EXIT_FAILURE;
479 result |= EXITING_WITH_STATUS | EXITING_WITH_MESSAGE;
480 }
481
482 if (exitcode && (result & EXITING_WITH_STATUS))
483 *exitcode = ex;
484 if (sigstatus && (result & EXITING_WITH_SIGNAL))
485 *sigstatus = sig;
486
487 return result;
488}
489
490#define unknown_longjmp_status(status) \
491 rb_bug("Unknown longjmp status %d", status)
492
493static int
494error_handle(rb_execution_context_t *ec, VALUE errinfo, enum ruby_tag_type ex)
495{
496 int status = EXIT_FAILURE;
497
498 if (rb_ec_set_raised(ec))
499 return EXIT_FAILURE;
500 switch (ex & TAG_MASK) {
501 case 0:
502 status = EXIT_SUCCESS;
503 break;
504
505 case TAG_RETURN:
506 error_pos(Qnil);
507 warn_print("unexpected return\n");
508 break;
509 case TAG_NEXT:
510 error_pos(Qnil);
511 warn_print("unexpected next\n");
512 break;
513 case TAG_BREAK:
514 error_pos(Qnil);
515 warn_print("unexpected break\n");
516 break;
517 case TAG_REDO:
518 error_pos(Qnil);
519 warn_print("unexpected redo\n");
520 break;
521 case TAG_RETRY:
522 error_pos(Qnil);
523 warn_print("retry outside of rescue clause\n");
524 break;
525 case TAG_THROW:
526 /* TODO: fix me */
527 error_pos(Qnil);
528 warn_print("unexpected throw\n");
529 break;
530 case TAG_RAISE:
531 if (!(exiting_split(errinfo, &status, NULL) & EXITING_WITH_MESSAGE)) {
532 break;
533 }
534 /* fallthrough */
535 case TAG_FATAL:
536 rb_ec_error_print(ec, errinfo);
537 break;
538 default:
539 unknown_longjmp_status(ex);
540 break;
541 }
542 rb_ec_reset_raised(ec);
543 return status;
544}
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1675
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define UNREACHABLE
Old name of RBIMPL_UNREACHABLE.
Definition assume.h:28
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:203
#define FIX2INT
Old name of RB_FIX2INT.
Definition int.h:41
#define T_MODULE
Old name of RUBY_T_MODULE.
Definition value_type.h:70
#define Qtrue
Old name of RUBY_Qtrue.
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define NIL_P
Old name of RB_NIL_P.
#define FIXNUM_P
Old name of RB_FIXNUM_P.
void rb_raise(VALUE exc, const char *fmt,...)
Exception entry point.
Definition error.c:3148
VALUE rb_eSystemExit
SystemExit exception.
Definition error.c:1084
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1089
VALUE rb_eArgError
ArgumentError exception.
Definition error.c:1092
VALUE rb_eException
Mother of all exceptions.
Definition error.c:1083
VALUE rb_eSysStackError
SystemStackError exception.
Definition eval.c:50
VALUE rb_eSystemCallError
SystemCallError exception.
Definition error.c:1111
VALUE rb_eSignal
SignalException exception.
Definition error.c:1086
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:84
VALUE rb_obj_is_instance_of(VALUE obj, VALUE klass)
Queries if the given object is a direct instance of the given class.
Definition object.c:731
VALUE rb_obj_is_kind_of(VALUE obj, VALUE klass)
Queries if the given object is an instance (of possibly descendants) of the given class.
Definition object.c:787
#define rb_strlen_lit(str)
Length of a string literal.
Definition string.h:1692
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1514
VALUE rb_attr_get(VALUE obj, ID name)
Identical to rb_ivar_get()
Definition variable.c:1226
VALUE rb_ivar_get(VALUE obj, ID name)
Identical to rb_iv_get(), except it accepts the name as an ID instead of a C string.
Definition variable.c:1218
VALUE rb_class_name(VALUE obj)
Queries the name of the given object's class.
Definition variable.c:310
VALUE rb_check_funcall(VALUE recv, ID mid, int argc, const VALUE *argv)
Identical to rb_funcallv(), except it returns RUBY_Qundef instead of raising rb_eNoMethodError.
Definition vm_eval.c:665
VALUE rb_sprintf(const char *fmt,...)
Ruby's extended sprintf(3).
Definition sprintf.c:1219
VALUE rb_str_catf(VALUE dst, const char *fmt,...)
Identical to rb_sprintf(), except it renders the output to the specified object rather than creating ...
Definition sprintf.c:1242
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:68
#define RARRAY_AREF(a, i)
Definition rarray.h:583
#define RTEST
This is an old name of RB_TEST.
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52