Tempfile document updated.
[ruby.git] / eval_error.c
blobf3d05a1043fea74e89bf8702cbd248b220273ada
1 /* -*-c-*- */
2 /*
3 * included by eval.c
4 */
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) \
18 #else
19 #define warn_print(x) rb_write_error(x)
20 #endif
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);
29 static void
30 error_pos(const VALUE str)
32 VALUE pos = error_pos_str();
33 if (!NIL_P(pos)) {
34 write_warn_str(str, pos);
38 static VALUE
39 error_pos_str(void)
41 int sourceline;
42 VALUE sourcefile = rb_source_location(&sourceline);
44 if (!NIL_P(sourcefile)) {
45 ID caller_name;
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));
54 else {
55 return rb_sprintf("%"PRIsVALUE":%d: ", sourcefile, sourceline);
58 return Qnil;
61 static void
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);
69 return;
71 else {
72 bt = rb_backtrace_to_str_ary(bt);
75 rb_check_funcall(info, set_backtrace, 1, &bt);
78 #define CSI_BEGIN "\033["
79 #define CSI_SGR "m"
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;
85 static void
86 print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, const VALUE str, int highlight)
88 long elen = 0;
89 VALUE mesg;
91 if (NIL_P(errat) || RARRAY_LEN(errat) == 0 ||
92 NIL_P(mesg = RARRAY_AREF(errat, 0))) {
93 error_pos(str);
95 else {
96 write_warn_str(str, mesg);
97 write_warn(str, ": ");
100 if (!NIL_P(emesg)) {
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");
110 else {
111 VALUE epath;
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");
120 else {
121 write_warn_str(str, emesg);
122 write_warn(str, "\n");
127 VALUE
128 rb_decorate_message(const VALUE eclass, VALUE emesg, int highlight)
130 const char *einfo = "";
131 long elen = 0;
132 rb_encoding *eenc;
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);
140 else {
141 eenc = NULL;
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);
148 else {
149 VALUE epath;
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);
157 else {
158 /* emesg is a String instance */
159 const char *tail = 0;
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 */
168 else {
169 write_warn_str(str, emesg);
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);
179 write_warn(str, ")");
180 if (highlight) write_warn(str, reset);
182 if (tail && einfo+elen > tail) {
183 if (!highlight) {
184 write_warn(str, "\n");
185 write_warn_enc(str, tail, einfo+elen-tail, eenc);
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;
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;
213 RB_GC_GUARD(emesg);
215 return str;
218 static void
219 print_backtrace(const VALUE eclass, const VALUE errat, const VALUE str, int reverse, long backtrace_limit)
221 if (!NIL_P(errat)) {
222 long i;
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) :
227 len - 1)) +
228 (len < threshold ? 0 : 9) + 1);
230 long skip_start = -1, skip_len = 0;
232 // skip for stackoverflow
233 if (eclass == rb_eSysStackError) {
234 long trace_head = 9;
235 long trace_tail = 4;
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));
252 i += skip_len;
253 if (i >= len) break;
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);
267 static int
268 shown_cause_p(VALUE cause, VALUE *shown_causes)
270 VALUE shown = *shown_causes;
271 if (!shown) {
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);
276 return FALSE;
279 static void
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);
288 if (reverse) {
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));
293 else {
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);
301 void
302 rb_exc_check_circular_cause(VALUE exc)
304 VALUE cause = exc, shown_causes = 0;
305 do {
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)));
312 void
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;
319 if (NIL_P(errinfo))
320 return;
322 if (UNDEF_P(errat)) {
323 errat = Qnil;
325 eclass = CLASS_OF(errinfo);
326 if (reverse) {
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);
339 #undef APPEND
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));
347 else {
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);
354 static void
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);
366 if (NIL_P(errinfo))
367 return;
368 rb_ec_raised_clear(ec);
370 EC_PUSH_TAG(ec);
371 if (EC_EXEC_TAG() == TAG_NONE) {
372 errat = rb_get_backtrace(errinfo);
374 if (UNDEF_P(emesg)) {
375 emesg = Qnil;
376 emesg = rb_get_detailed_message(errinfo, opt);
379 if (!written) {
380 written = true;
381 rb_error_write(errinfo, emesg, errat, str, opt, highlight, Qfalse);
384 EC_POP_TAG();
385 ec->errinfo = errinfo;
386 rb_ec_raised_set(ec, raised_flag);
389 void
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) ( \
397 is_mod ? \
398 undef_mesg_for(v, "module") : \
399 undef_mesg_for(v, "class"))
401 void
402 rb_print_undef(VALUE klass, ID id, rb_method_visibility_t visi)
404 const int is_mod = RB_TYPE_P(klass, T_MODULE);
405 VALUE mesg;
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));
416 void
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) ( \
425 is_mod ? \
426 inaccessible_mesg_for(v, "module") : \
427 inaccessible_mesg_for(v, "class"))
429 void
430 rb_print_inaccessible(VALUE klass, ID id, rb_method_visibility_t visi)
432 const int is_mod = RB_TYPE_P(klass, T_MODULE);
433 VALUE mesg;
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));
444 static int
445 sysexit_status(VALUE err)
447 VALUE st = rb_ivar_get(err, id_status);
448 return NUM2INT(st);
451 enum {
452 EXITING_WITH_MESSAGE = 1,
453 EXITING_WITH_STATUS = 2,
454 EXITING_WITH_SIGNAL = 4
456 static int
457 exiting_split(VALUE errinfo, volatile int *exitcode, volatile int *sigstatus)
459 int ex = EXIT_SUCCESS;
460 VALUE signo;
461 int sig = 0;
462 int result = 0;
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 */
490 else {
491 ex = EXIT_FAILURE;
492 result |= EXITING_WITH_STATUS | EXITING_WITH_MESSAGE;
495 if (exitcode && (result & EXITING_WITH_STATUS))
496 *exitcode = ex;
497 if (sigstatus && (result & EXITING_WITH_SIGNAL))
498 *sigstatus = sig;
500 return result;
503 #define unknown_longjmp_status(status) \
504 rb_bug("Unknown longjmp status %d", status)
506 static int
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))
512 return EXIT_FAILURE;
513 switch (ex & TAG_MASK) {
514 case 0:
515 status = EXIT_SUCCESS;
516 break;
518 case TAG_RETURN:
519 error_pos(Qnil);
520 warn_print("unexpected return\n");
521 break;
522 case TAG_NEXT:
523 error_pos(Qnil);
524 warn_print("unexpected next\n");
525 break;
526 case TAG_BREAK:
527 error_pos(Qnil);
528 warn_print("unexpected break\n");
529 break;
530 case TAG_REDO:
531 error_pos(Qnil);
532 warn_print("unexpected redo\n");
533 break;
534 case TAG_RETRY:
535 error_pos(Qnil);
536 warn_print("retry outside of rescue clause\n");
537 break;
538 case TAG_THROW:
539 /* TODO: fix me */
540 error_pos(Qnil);
541 warn_print("unexpected throw\n");
542 break;
543 case TAG_RAISE:
544 if (!(exiting_split(errinfo, &status, NULL) & EXITING_WITH_MESSAGE)) {
545 break;
547 /* fallthrough */
548 case TAG_FATAL:
549 rb_ec_error_print(ec, errinfo);
550 break;
551 default:
552 unknown_longjmp_status(ex);
553 break;
555 rb_ec_reset_raised(ec);
556 return status;