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