Ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5ad0e4688e963d9de019557c78feed9)
eval_error.c
Go to the documentation of this file.
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 {
71 }
72 }
73 rb_check_funcall(info, set_backtrace, 1, &bt);
74}
75
76static void
77error_print(rb_execution_context_t *ec)
78{
80}
81
82static void
83write_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;
99 if (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))
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
126static const char underline[] = CSI_BEGIN"1;4"CSI_SGR;
127static const char bold[] = CSI_BEGIN"1"CSI_SGR;
128static const char reset[] = CSI_BEGIN""CSI_SGR;
129
130static void
131print_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
230static void
231print_backtrace(const VALUE eclass, const VALUE errat, const VALUE str, int reverse)
232{
233 if (!NIL_P(errat)) {
234 long i;
235 long len = RARRAY_LEN(errat);
236 int skip = eclass == rb_eSysStackError;
237 const int threshold = 1000000000;
238 int width = (len <= 1) ? INT_MIN : ((int)log10((double)(len > threshold ?
239 ((len - 1) / threshold) :
240 len - 1)) +
241 (len < threshold ? 0 : 9) + 1);
242
243#define TRACE_MAX (TRACE_HEAD+TRACE_TAIL+5)
244#define TRACE_HEAD 8
245#define TRACE_TAIL 5
246
247 for (i = 1; i < len; i++) {
248 VALUE line = RARRAY_AREF(errat, reverse ? len - i : i);
249 if (RB_TYPE_P(line, T_STRING)) {
250 VALUE bt = rb_str_new_cstr("\t");
251 if (reverse) rb_str_catf(bt, "%*ld: ", width, len - i);
252 write_warn_str(str, rb_str_catf(bt, "from %"PRIsVALUE"\n", line));
253 }
254 if (skip && i == TRACE_HEAD && len > TRACE_MAX) {
255 write_warn_str(str, rb_sprintf("\t ... %ld levels...\n",
257 i = len - TRACE_TAIL;
258 }
259 }
260 }
261}
262
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 highlight, VALUE reverse, 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_message(cause);
286 if (reverse) {
287 show_cause(cause, str, highlight, reverse, shown_causes);
288 print_backtrace(eclass, errat, str, TRUE);
289 print_errinfo(eclass, errat, emesg, str, highlight!=0);
290 }
291 else {
292 print_errinfo(eclass, errat, emesg, str, highlight!=0);
293 print_backtrace(eclass, errat, str, FALSE);
294 show_cause(cause, str, highlight, reverse, shown_causes);
295 }
296 }
297}
298
299void
300rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE highlight, VALUE reverse)
301{
302 volatile VALUE eclass;
303 VALUE shown_causes = 0;
304
305 if (NIL_P(errinfo))
306 return;
307
308 if (errat == Qundef) {
309 errat = Qnil;
310 }
311 eclass = CLASS_OF(errinfo);
312 if (NIL_P(reverse) || NIL_P(highlight)) {
313 VALUE tty = (VALUE)rb_stderr_tty_p();
314 if (NIL_P(reverse)) reverse = tty;
315 if (NIL_P(highlight)) highlight = tty;
316 }
317 if (reverse) {
318 static const char traceback[] = "Traceback "
319 "(most recent call last):\n";
320 const int bold_part = rb_strlen_lit("Traceback");
321 char buff[sizeof(traceback)+sizeof(bold)+sizeof(reset)-2], *p = buff;
322 const char *msg = traceback;
323 long len = sizeof(traceback) - 1;
324 if (highlight) {
325#define APPEND(s, l) (memcpy(p, s, l), p += (l))
326 APPEND(bold, sizeof(bold)-1);
327 APPEND(traceback, bold_part);
328 APPEND(reset, sizeof(reset)-1);
329 APPEND(traceback + bold_part, sizeof(traceback)-bold_part-1);
330#undef APPEND
331 len = p - (msg = buff);
332 }
333 write_warn2(str, msg, len);
334 show_cause(errinfo, str, highlight, reverse, &shown_causes);
335 print_backtrace(eclass, errat, str, TRUE);
336 print_errinfo(eclass, errat, emesg, str, highlight!=0);
337 }
338 else {
339 print_errinfo(eclass, errat, emesg, str, highlight!=0);
340 print_backtrace(eclass, errat, str, FALSE);
341 show_cause(errinfo, str, highlight, reverse, &shown_causes);
342 }
343}
344
345void
346rb_ec_error_print(rb_execution_context_t * volatile ec, volatile VALUE errinfo)
347{
348 volatile uint8_t raised_flag = ec->raised_flag;
349 volatile VALUE errat = Qundef;
350 volatile VALUE emesg = Qundef;
351 volatile bool written = false;
352
353 if (NIL_P(errinfo))
354 return;
356
357 EC_PUSH_TAG(ec);
358 if (EC_EXEC_TAG() == TAG_NONE) {
359 errat = rb_get_backtrace(errinfo);
360 }
361 if (emesg == Qundef) {
362 emesg = Qnil;
363 emesg = rb_get_message(errinfo);
364 }
365
366 if (!written) {
367 written = true;
368 rb_error_write(errinfo, emesg, errat, Qnil, Qnil, Qnil);
369 }
370
371 EC_POP_TAG();
372 ec->errinfo = errinfo;
373 rb_ec_raised_set(ec, raised_flag);
374}
375
376#define undef_mesg_for(v, k) rb_fstring_lit("undefined"v" method `%1$s' for "k" `%2$s'")
377#define undef_mesg(v) ( \
378 is_mod ? \
379 undef_mesg_for(v, "module") : \
380 undef_mesg_for(v, "class"))
381
382void
384{
385 const int is_mod = RB_TYPE_P(klass, T_MODULE);
386 VALUE mesg;
387 switch (visi & METHOD_VISI_MASK) {
389 case METHOD_VISI_PUBLIC: mesg = undef_mesg(""); break;
390 case METHOD_VISI_PRIVATE: mesg = undef_mesg(" private"); break;
391 case METHOD_VISI_PROTECTED: mesg = undef_mesg(" protected"); break;
392 default: UNREACHABLE;
393 }
395}
396
397void
399{
400 const int is_mod = RB_TYPE_P(klass, T_MODULE);
402}
403
404#define inaccessible_mesg_for(v, k) rb_fstring_lit("method `%1$s' for "k" `%2$s' is "v)
405#define inaccessible_mesg(v) ( \
406 is_mod ? \
407 inaccessible_mesg_for(v, "module") : \
408 inaccessible_mesg_for(v, "class"))
409
410void
412{
413 const int is_mod = RB_TYPE_P(klass, T_MODULE);
414 VALUE mesg;
415 switch (visi & METHOD_VISI_MASK) {
417 case METHOD_VISI_PUBLIC: mesg = inaccessible_mesg(""); break;
418 case METHOD_VISI_PRIVATE: mesg = inaccessible_mesg(" private"); break;
419 case METHOD_VISI_PROTECTED: mesg = inaccessible_mesg(" protected"); break;
420 default: UNREACHABLE;
421 }
423}
424
425static int
426sysexit_status(VALUE err)
427{
429 return NUM2INT(st);
430}
431
432#define unknown_longjmp_status(status) \
433 rb_bug("Unknown longjmp status %d", status)
434
435static int
436error_handle(rb_execution_context_t *ec, int ex)
437{
438 int status = EXIT_FAILURE;
439
440 if (rb_ec_set_raised(ec))
441 return EXIT_FAILURE;
442 switch (ex & TAG_MASK) {
443 case 0:
444 status = EXIT_SUCCESS;
445 break;
446
447 case TAG_RETURN:
448 error_pos(Qnil);
449 warn_print("unexpected return\n");
450 break;
451 case TAG_NEXT:
452 error_pos(Qnil);
453 warn_print("unexpected next\n");
454 break;
455 case TAG_BREAK:
456 error_pos(Qnil);
457 warn_print("unexpected break\n");
458 break;
459 case TAG_REDO:
460 error_pos(Qnil);
461 warn_print("unexpected redo\n");
462 break;
463 case TAG_RETRY:
464 error_pos(Qnil);
465 warn_print("retry outside of rescue clause\n");
466 break;
467 case TAG_THROW:
468 /* TODO: fix me */
469 error_pos(Qnil);
470 warn_print("unexpected throw\n");
471 break;
472 case TAG_RAISE: {
473 VALUE errinfo = ec->errinfo;
474 if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
475 status = sysexit_status(errinfo);
476 }
477 else if (rb_obj_is_instance_of(errinfo, rb_eSignal) &&
478 rb_ivar_get(errinfo, id_signo) != INT2FIX(SIGSEGV)) {
479 /* no message when exiting by signal */
480 }
481 else {
482 rb_ec_error_print(ec, errinfo);
483 }
484 break;
485 }
486 case TAG_FATAL:
487 error_print(ec);
488 break;
489 default:
491 break;
492 }
494 return status;
495}
struct RIMemo * ptr
Definition: debug.c:65
char str[HTML_ESCAPE_MAX_LEN+1]
Definition: escape.c:18
#define undef_mesg(v)
Definition: eval_error.c:377
void rb_ec_error_print(rb_execution_context_t *volatile ec, volatile VALUE errinfo)
Definition: eval_error.c:346
#define warn_print(x)
Definition: eval_error.c:17
#define inaccessible_mesg(v)
Definition: eval_error.c:405
#define write_warn(str, x)
Definition: eval_error.c:6
void rb_print_undef(VALUE klass, ID id, rb_method_visibility_t visi)
Definition: eval_error.c:383
#define write_warn_str(str, x)
Definition: eval_error.c:22
void rb_print_inaccessible(VALUE klass, ID id, rb_method_visibility_t visi)
Definition: eval_error.c:411
#define TRACE_HEAD
void rb_print_undef_str(VALUE klass, VALUE name)
Definition: eval_error.c:398
#define write_warn2(str, x, l)
Definition: eval_error.c:8
#define TRACE_MAX
#define unknown_longjmp_status(status)
Definition: eval_error.c:432
#define CSI_BEGIN
Definition: eval_error.c:123
#define CSI_SGR
Definition: eval_error.c:124
#define TRACE_TAIL
#define APPEND(s, l)
ID rb_frame_callee(void)
The name of the current method.
Definition: eval.c:1200
VALUE rb_eSysStackError
Definition: eval.c:35
VALUE rb_get_backtrace(VALUE info)
Definition: error.c:1231
void rb_error_write(VALUE errinfo, VALUE emesg, VALUE errat, VALUE str, VALUE highlight, VALUE reverse)
Definition: eval_error.c:300
VALUE rb_eSystemExit
Definition: error.c:917
VALUE rb_eRuntimeError
Definition: error.c:922
VALUE rb_eException
Definition: error.c:916
VALUE rb_get_message(VALUE exc)
Definition: error.c:1053
VALUE rb_exc_set_backtrace(VALUE exc, VALUE bt)
Definition: error.c:1312
#define id_cause
Definition: error.c:948
VALUE rb_eSignal
Definition: error.c:919
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition: object.c:78
VALUE rb_obj_is_instance_of(VALUE, VALUE)
Determines if obj is an instance of c.
Definition: object.c:675
VALUE rb_obj_is_kind_of(VALUE, VALUE)
Determines if obj is a kind of c.
Definition: object.c:692
const char * name
Definition: nkf.c:208
unsigned char buf[MIME_BUF_SIZE]
Definition: nkf.c:4322
VALUE rb_class_name(VALUE)
Definition: variable.c:274
#define RARRAY_LEN(a)
int rb_ec_set_raised(rb_execution_context_t *ec)
Definition: thread.c:2343
void * memchr(const void *, int, size_t)
#define TAG_RAISE
#define RSTRING_LEN(str)
VALUE rb_backtrace_to_str_ary(VALUE obj)
Definition: vm_backtrace.c:620
#define TAG_NONE
const char * ruby_escaped_char(int c)
Definition: string.c:5848
enum ruby_tag_type st
#define INT_MIN
size_t strlen(const char *)
#define T_STRING
#define TAG_RETRY
__uint8_t uint8_t
#define Qundef
#define rb_ec_raised_set(ec, f)
VALUE rb_ivar_get(VALUE, ID)
Definition: variable.c:1070
void rb_write_error_str(VALUE mesg)
Definition: io.c:7940
#define UNREACHABLE
#define RSTRING_PTR(str)
int snprintf(char *__restrict__, size_t, const char *__restrict__,...) __attribute__((__format__(__printf__
VALUE rb_ident_hash_new(void)
Definition: hash.c:4278
#define NIL_P(v)
#define id_status
VALUE rb_check_funcall(VALUE, ID, int, const VALUE *)
Definition: vm_eval.c:505
VALUE rb_str_cat(VALUE, const char *, long)
Definition: string.c:2812
#define ID2SYM(x)
#define EC_EXEC_TAG()
VALUE rb_hash_has_key(VALUE hash, VALUE key)
Definition: hash.c:3507
#define TAG_MASK
const char const char *typedef unsigned long VALUE
#define EC_PUSH_TAG(ec)
#define EXIT_SUCCESS
@ METHOD_VISI_PROTECTED
#define T_MODULE
#define TAG_THROW
uint32_t i
__inline__ const void *__restrict__ size_t len
#define EXIT_FAILURE
#define TAG_FATAL
int rb_backtrace_p(VALUE obj)
Definition: vm_backtrace.c:446
#define TAG_REDO
#define rb_ec_raised_clear(ec)
#define NUM2INT(x)
#define PRIsVALUE
#define rb_method_basic_definition_p(klass, mid)
#define EC_POP_TAG()
#define rb_intern(str)
double log10(double)
VALUE rb_str_catf(VALUE, const char *,...) __attribute__((format(printf
#define TAG_BREAK
#define TRUE
#define FALSE
#define Qtrue
#define rb_strlen_lit(str)
const struct rb_call_cache * cc
#define id_signo
VALUE rb_attr_get(VALUE, ID)
Definition: variable.c:1084
#define Qnil
void rb_write_error2(const char *, long)
Definition: io.c:7915
int rb_stderr_tty_p(void)
Definition: io.c:7962
#define TAG_RETURN
VALUE rb_source_location(int *pline)
Definition: vm.c:1360
#define RB_TYPE_P(obj, type)
#define INT2FIX(i)
#define TAG_NEXT
int rb_ec_reset_raised(rb_execution_context_t *ec)
Definition: thread.c:2353
#define CLASS_OF(v)
VALUE rb_hash_aset(VALUE, VALUE, VALUE)
Definition: hash.c:2852
#define SIGSEGV
VALUE rb_sprintf(const char *,...) __attribute__((format(printf
unsigned long ID
const rb_iseq_t const VALUE exc
#define rb_name_err_raise_str(mesg, recv, name)
#define RARRAY_AREF(a, i)
#define rb_str_new_cstr(str)
unsigned long VALUE
Definition: ruby.h:102
#define rb_id2str(id)
Definition: vm_backtrace.c:30