Ruby  3.1.4p223 (2023-03-30 revision HEAD)
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 
25 static VALUE error_pos_str(void);
26 
27 static void
28 error_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 
36 static VALUE
37 error_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 
59 static void
60 set_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 static void
77 error_print(rb_execution_context_t *ec)
78 {
79  rb_ec_error_print(ec, ec->errinfo);
80 }
81 
82 static void
83 write_warnq(VALUE out, VALUE str, const char *ptr, long len)
84 {
85  if (NIL_P(out)) {
86  const char *beg = ptr;
87  const long olen = len;
88  for (; len > 0; --len, ++ptr) {
89  unsigned char c = *ptr;
90  switch (c) {
91  case '\n': case '\t': continue;
92  }
93  if (rb_iscntrl(c)) {
94  char buf[5];
95  const char *cc = 0;
96  if (ptr > beg) rb_write_error2(beg, ptr - beg);
97  beg = ptr + 1;
98  cc = ruby_escaped_char(c);
99  if (cc) {
100  rb_write_error2(cc, strlen(cc));
101  }
102  else {
103  rb_write_error2(buf, snprintf(buf, sizeof(buf), "\\x%02X", c));
104  }
105  }
106  else if (c == '\\') {
107  rb_write_error2(beg, ptr - beg + 1);
108  beg = ptr;
109  }
110  }
111  if (ptr > beg) {
112  if (beg == RSTRING_PTR(str) && olen == RSTRING_LEN(str))
113  rb_write_error_str(str);
114  else
115  rb_write_error2(beg, ptr - beg);
116  }
117  }
118  else {
119  rb_str_cat(out, ptr, len);
120  }
121 }
122 
123 #define CSI_BEGIN "\033["
124 #define CSI_SGR "m"
125 
126 static const char underline[] = CSI_BEGIN"1;4"CSI_SGR;
127 static const char bold[] = CSI_BEGIN"1"CSI_SGR;
128 static const char reset[] = CSI_BEGIN""CSI_SGR;
129 
130 static void
131 print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, const VALUE str, int highlight)
132 {
133  const char *einfo = "";
134  long elen = 0;
135  VALUE mesg;
136 
137  if (emesg != Qundef) {
138  if (NIL_P(errat) || RARRAY_LEN(errat) == 0 ||
139  NIL_P(mesg = RARRAY_AREF(errat, 0))) {
140  error_pos(str);
141  }
142  else {
143  write_warn_str(str, mesg);
144  write_warn(str, ": ");
145  }
146 
147  if (!NIL_P(emesg)) {
148  einfo = RSTRING_PTR(emesg);
149  elen = RSTRING_LEN(emesg);
150  }
151  }
152 
153  if (eclass == rb_eRuntimeError && elen == 0) {
154  if (highlight) write_warn(str, underline);
155  write_warn(str, "unhandled exception");
156  if (highlight) write_warn(str, reset);
157  write_warn2(str, "\n", 1);
158  }
159  else {
160  VALUE epath;
161 
162  epath = rb_class_name(eclass);
163  if (elen == 0) {
164  if (highlight) write_warn(str, underline);
165  write_warn_str(str, epath);
166  if (highlight) write_warn(str, reset);
167  write_warn(str, "\n");
168  }
169  else {
170  /* emesg is a String instance */
171  const char *tail = 0;
172 
173  if (highlight) write_warn(str, bold);
174  if (RSTRING_PTR(epath)[0] == '#')
175  epath = 0;
176  if ((tail = memchr(einfo, '\n', elen)) != 0) {
177  write_warnq(str, emesg, einfo, tail - einfo);
178  tail++; /* skip newline */
179  }
180  else {
181  write_warnq(str, emesg, einfo, elen);
182  }
183  if (epath) {
184  write_warn(str, " (");
185  if (highlight) write_warn(str, underline);
186  write_warn_str(str, epath);
187  if (highlight) {
188  write_warn(str, reset);
189  write_warn(str, bold);
190  }
191  write_warn2(str, ")", 1);
192  if (highlight) write_warn(str, reset);
193  write_warn2(str, "\n", 1);
194  }
195  if (tail && einfo+elen > tail) {
196  if (!highlight) {
197  write_warnq(str, emesg, tail, einfo+elen-tail);
198  if (einfo[elen-1] != '\n') write_warn2(str, "\n", 1);
199  }
200  else {
201  elen -= tail - einfo;
202  einfo = tail;
203  while (elen > 0) {
204  tail = memchr(einfo, '\n', elen);
205  if (!tail || tail > einfo) {
206  write_warn(str, bold);
207  write_warnq(str, emesg, einfo, tail ? tail-einfo : elen);
208  write_warn(str, reset);
209  if (!tail) {
210  write_warn2(str, "\n", 1);
211  break;
212  }
213  }
214  elen -= tail - einfo;
215  einfo = tail;
216  do ++tail; while (tail < einfo+elen && *tail == '\n');
217  write_warnq(str, emesg, einfo, tail-einfo);
218  elen -= tail - einfo;
219  einfo = tail;
220  }
221  }
222  }
223  else if (!epath) {
224  write_warn2(str, "\n", 1);
225  }
226  }
227  }
228 }
229 
230 static void
231 print_backtrace(const VALUE eclass, const VALUE errat, const VALUE str, int reverse, long backtrace_limit)
232 {
233  if (!NIL_P(errat)) {
234  long i;
235  long len = RARRAY_LEN(errat);
236  const int threshold = 1000000000;
237  int width = (len <= 1) ? INT_MIN : ((int)log10((double)(len > threshold ?
238  ((len - 1) / threshold) :
239  len - 1)) +
240  (len < threshold ? 0 : 9) + 1);
241 
242  long skip_start = -1, skip_len = 0;
243 
244  // skip for stackoverflow
245  if (eclass == rb_eSysStackError) {
246  long trace_head = 9;
247  long trace_tail = 4;
248  long trace_max = trace_head + trace_tail + 5;
249  if (len > trace_max) {
250  skip_start = trace_head;
251  skip_len = len - trace_max + 5;
252  }
253  }
254 
255  // skip for explicit limit
256  if (backtrace_limit >= 0 && len > backtrace_limit + 2) {
257  skip_start = backtrace_limit + 1;
258  skip_len = len - skip_start;
259  }
260 
261  for (i = 1; i < len; i++) {
262  if (i == skip_start) {
263  write_warn_str(str, rb_sprintf("\t ... %ld levels...\n", skip_len));
264  i += skip_len;
265  if (i >= len) break;
266  }
267  VALUE line = RARRAY_AREF(errat, reverse ? len - i : i);
268  if (RB_TYPE_P(line, T_STRING)) {
269  VALUE bt = rb_str_new_cstr("\t");
270  if (reverse) rb_str_catf(bt, "%*ld: ", width, len - i);
271  write_warn_str(str, rb_str_catf(bt, "from %"PRIsVALUE"\n", line));
272  }
273  }
274  }
275 }
276 
277 VALUE rb_get_message(VALUE exc);
278 
279 static int
280 shown_cause_p(VALUE cause, VALUE *shown_causes)
281 {
282  VALUE shown = *shown_causes;
283  if (!shown) {
284  *shown_causes = shown = rb_obj_hide(rb_ident_hash_new());
285  }
286  if (rb_hash_has_key(shown, cause)) return TRUE;
287  rb_hash_aset(shown, cause, Qtrue);
288  return FALSE;
289 }
290 
291 static void
292 show_cause(VALUE errinfo, VALUE str, VALUE highlight, VALUE reverse, long backtrace_limit, VALUE *shown_causes)
293 {
294  VALUE cause = rb_attr_get(errinfo, id_cause);
295  if (!NIL_P(cause) && rb_obj_is_kind_of(cause, rb_eException) &&
296  !shown_cause_p(cause, shown_causes)) {
297  volatile VALUE eclass = CLASS_OF(cause);
298  VALUE errat = rb_get_backtrace(cause);
299  VALUE emesg = rb_get_message(cause);
300  if (reverse) {
301  show_cause(cause, str, highlight, reverse, backtrace_limit, shown_causes);
302  print_backtrace(eclass, errat, str, TRUE, backtrace_limit);
303  print_errinfo(eclass, errat, emesg, str, highlight!=0);
304  }
305  else {
306  print_errinfo(eclass, errat, emesg, str, highlight!=0);
307  print_backtrace(eclass, errat, str, FALSE, backtrace_limit);
308  show_cause(cause, str, highlight, reverse, backtrace_limit, shown_causes);
309  }
310  }
311 }
312 
313 void
314 rb_exc_check_circular_cause(VALUE exc)
315 {
316  VALUE cause = exc, shown_causes = 0;
317  do {
318  if (shown_cause_p(cause, &shown_causes)) {
319  rb_raise(rb_eArgError, "circular causes");
320  }
321  } while (!NIL_P(cause = rb_attr_get(cause, id_cause)));
322 }
323 
324 void
325 rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE highlight, VALUE reverse)
326 {
327  volatile VALUE eclass;
328  VALUE shown_causes = 0;
329  long backtrace_limit = rb_backtrace_length_limit;
330 
331  if (NIL_P(errinfo))
332  return;
333 
334  if (errat == Qundef) {
335  errat = Qnil;
336  }
337  eclass = CLASS_OF(errinfo);
338  if (NIL_P(reverse)) reverse = Qfalse;
339  if (NIL_P(highlight)) {
340  VALUE tty = (VALUE)rb_stderr_tty_p();
341  if (NIL_P(highlight)) highlight = tty;
342  }
343  if (reverse) {
344  static const char traceback[] = "Traceback "
345  "(most recent call last):\n";
346  const int bold_part = rb_strlen_lit("Traceback");
347  char buff[sizeof(traceback)+sizeof(bold)+sizeof(reset)-2], *p = buff;
348  const char *msg = traceback;
349  long len = sizeof(traceback) - 1;
350  if (highlight) {
351 #define APPEND(s, l) (memcpy(p, s, l), p += (l))
352  APPEND(bold, sizeof(bold)-1);
353  APPEND(traceback, bold_part);
354  APPEND(reset, sizeof(reset)-1);
355  APPEND(traceback + bold_part, sizeof(traceback)-bold_part-1);
356 #undef APPEND
357  len = p - (msg = buff);
358  }
359  write_warn2(str, msg, len);
360  show_cause(errinfo, str, highlight, reverse, backtrace_limit, &shown_causes);
361  print_backtrace(eclass, errat, str, TRUE, backtrace_limit);
362  print_errinfo(eclass, errat, emesg, str, highlight!=0);
363  }
364  else {
365  print_errinfo(eclass, errat, emesg, str, highlight!=0);
366  print_backtrace(eclass, errat, str, FALSE, backtrace_limit);
367  show_cause(errinfo, str, highlight, reverse, backtrace_limit, &shown_causes);
368  }
369 }
370 
371 void
372 rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo)
373 {
374  volatile uint8_t raised_flag = ec->raised_flag;
375  volatile VALUE errat = Qundef;
376  volatile VALUE emesg = Qundef;
377  volatile bool written = false;
378 
379  if (NIL_P(errinfo))
380  return;
381  rb_ec_raised_clear(ec);
382 
383  EC_PUSH_TAG(ec);
384  if (EC_EXEC_TAG() == TAG_NONE) {
385  errat = rb_get_backtrace(errinfo);
386  }
387  if (emesg == Qundef) {
388  emesg = Qnil;
389  emesg = rb_get_message(errinfo);
390  }
391 
392  if (!written) {
393  written = true;
394  rb_error_write(errinfo, emesg, errat, Qnil, Qnil, Qfalse);
395  }
396 
397  EC_POP_TAG();
398  ec->errinfo = errinfo;
399  rb_ec_raised_set(ec, raised_flag);
400 }
401 
402 #define undef_mesg_for(v, k) rb_fstring_lit("undefined"v" method `%1$s' for "k" `%2$s'")
403 #define undef_mesg(v) ( \
404  is_mod ? \
405  undef_mesg_for(v, "module") : \
406  undef_mesg_for(v, "class"))
407 
408 void
409 rb_print_undef(VALUE klass, ID id, rb_method_visibility_t visi)
410 {
411  const int is_mod = RB_TYPE_P(klass, T_MODULE);
412  VALUE mesg;
413  switch (visi & METHOD_VISI_MASK) {
414  case METHOD_VISI_UNDEF:
415  case METHOD_VISI_PUBLIC: mesg = undef_mesg(""); break;
416  case METHOD_VISI_PRIVATE: mesg = undef_mesg(" private"); break;
417  case METHOD_VISI_PROTECTED: mesg = undef_mesg(" protected"); break;
418  default: UNREACHABLE;
419  }
420  rb_name_err_raise_str(mesg, klass, ID2SYM(id));
421 }
422 
423 void
424 rb_print_undef_str(VALUE klass, VALUE name)
425 {
426  const int is_mod = RB_TYPE_P(klass, T_MODULE);
427  rb_name_err_raise_str(undef_mesg(""), klass, name);
428 }
429 
430 #define inaccessible_mesg_for(v, k) rb_fstring_lit("method `%1$s' for "k" `%2$s' is "v)
431 #define inaccessible_mesg(v) ( \
432  is_mod ? \
433  inaccessible_mesg_for(v, "module") : \
434  inaccessible_mesg_for(v, "class"))
435 
436 void
437 rb_print_inaccessible(VALUE klass, ID id, rb_method_visibility_t visi)
438 {
439  const int is_mod = RB_TYPE_P(klass, T_MODULE);
440  VALUE mesg;
441  switch (visi & METHOD_VISI_MASK) {
442  case METHOD_VISI_UNDEF:
443  case METHOD_VISI_PUBLIC: mesg = inaccessible_mesg(""); break;
444  case METHOD_VISI_PRIVATE: mesg = inaccessible_mesg("private"); break;
445  case METHOD_VISI_PROTECTED: mesg = inaccessible_mesg("protected"); break;
446  default: UNREACHABLE;
447  }
448  rb_name_err_raise_str(mesg, klass, ID2SYM(id));
449 }
450 
451 static int
452 sysexit_status(VALUE err)
453 {
454  VALUE st = rb_ivar_get(err, id_status);
455  return NUM2INT(st);
456 }
457 
458 #define unknown_longjmp_status(status) \
459  rb_bug("Unknown longjmp status %d", status)
460 
461 static int
462 error_handle(rb_execution_context_t *ec, int ex)
463 {
464  int status = EXIT_FAILURE;
465 
466  if (rb_ec_set_raised(ec))
467  return EXIT_FAILURE;
468  switch (ex & TAG_MASK) {
469  case 0:
470  status = EXIT_SUCCESS;
471  break;
472 
473  case TAG_RETURN:
474  error_pos(Qnil);
475  warn_print("unexpected return\n");
476  break;
477  case TAG_NEXT:
478  error_pos(Qnil);
479  warn_print("unexpected next\n");
480  break;
481  case TAG_BREAK:
482  error_pos(Qnil);
483  warn_print("unexpected break\n");
484  break;
485  case TAG_REDO:
486  error_pos(Qnil);
487  warn_print("unexpected redo\n");
488  break;
489  case TAG_RETRY:
490  error_pos(Qnil);
491  warn_print("retry outside of rescue clause\n");
492  break;
493  case TAG_THROW:
494  /* TODO: fix me */
495  error_pos(Qnil);
496  warn_print("unexpected throw\n");
497  break;
498  case TAG_RAISE: {
499  VALUE errinfo = ec->errinfo;
500  if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
501  status = sysexit_status(errinfo);
502  }
503  else if (rb_obj_is_instance_of(errinfo, rb_eSignal) &&
504  rb_ivar_get(errinfo, id_signo) != INT2FIX(SIGSEGV)) {
505  /* no message when exiting by signal */
506  }
507  else if (rb_obj_is_kind_of(errinfo, rb_eSystemCallError) &&
508  FIXNUM_P(rb_attr_get(errinfo, id_signo))) {
509  /* no message when exiting by error to be mapped to signal */
510  }
511  else {
512  rb_ec_error_print(ec, errinfo);
513  }
514  break;
515  }
516  case TAG_FATAL:
517  error_print(ec);
518  break;
519  default:
520  unknown_longjmp_status(ex);
521  break;
522  }
523  rb_ec_reset_raised(ec);
524  return status;
525 }
static int rb_iscntrl(int c)
Our own locale-insensitive version of iscntrl(3).
Definition: ctype.h:418
#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:30
#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 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:3025
VALUE rb_eSystemExit
SystemExit exception.
Definition: error.c:1092
VALUE rb_eRuntimeError
RuntimeError exception.
Definition: error.c:1097
VALUE rb_eArgError
ArgumentError exception.
Definition: error.c:1100
VALUE rb_eException
Mother of all exceptions.
Definition: error.c:1091
VALUE rb_eSysStackError
SystemStackError exception.
Definition: eval.c:49
VALUE rb_eSystemCallError
SystemCallError exception.
Definition: error.c:1119
VALUE rb_eSignal
SignalException exception.
Definition: error.c:1094
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition: object.c:82
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:695
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:731
ID rb_frame_callee(void)
Identical to rb_frame_this_func(), except it returns the named used to call the method.
Definition: eval.c:1045
VALUE rb_hash_aset(VALUE hash, VALUE key, VALUE val)
Inserts or replaces ("upsert"s) the objects into the given hash table.
Definition: hash.c:2903
void rb_write_error2(const char *str, long len)
Identical to rb_write_error(), except it additionally takes the message's length.
Definition: io.c:8371
VALUE rb_str_cat(VALUE dst, const char *src, long srclen)
Destructively appends the passed contents to the string.
Definition: string.c:3161
#define rb_strlen_lit(str)
Length of a string literal.
Definition: string.h:1756
VALUE rb_str_new_cstr(const char *ptr)
Identical to rb_str_new(), except it assumes the passed pointer is a pointer to a C string.
Definition: string.c:952
VALUE rb_attr_get(VALUE obj, ID name)
Identical to rb_ivar_get()
Definition: variable.c:1293
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:1285
VALUE rb_class_name(VALUE obj)
Queries the name of the given object's class.
Definition: variable.c:294
int rb_method_basic_definition_p(VALUE klass, ID mid)
Well...
Definition: vm_method.c:2643
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:664
ID rb_intern(const char *name)
Finds or creates a symbol of the given name.
Definition: symbol.c:782
VALUE rb_id2str(ID id)
Identical to rb_id2name(), except it returns a Ruby's String instead of C's.
Definition: symbol.c:935
VALUE rb_sprintf(const char *fmt,...)
Ruby's extended sprintf(3).
Definition: sprintf.c:1201
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:1241
#define RARRAY_LEN
Just another name of rb_array_len.
Definition: rarray.h:68
#define RARRAY_AREF(a, i)
Definition: rarray.h:588
static char * RSTRING_PTR(VALUE str)
Queries the contents pointer of the string.
Definition: rstring.h:497
static long RSTRING_LEN(VALUE str)
Queries the length of the string.
Definition: rstring.h:483
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
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition: value_type.h:375