Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | ** error.c - Exception class |
3 | | ** |
4 | | ** See Copyright Notice in mruby.h |
5 | | */ |
6 | | |
7 | | #include <errno.h> |
8 | | #include <stdlib.h> |
9 | | #include <mruby.h> |
10 | | #include <mruby/array.h> |
11 | | #include <mruby/irep.h> |
12 | | #include <mruby/proc.h> |
13 | | #include <mruby/string.h> |
14 | | #include <mruby/variable.h> |
15 | | #include <mruby/error.h> |
16 | | #include <mruby/class.h> |
17 | | #include <mruby/throw.h> |
18 | | #include <mruby/internal.h> |
19 | | #include <mruby/presym.h> |
20 | | |
21 | | void |
22 | | mrb_exc_mesg_set(mrb_state *mrb, struct RException *exc, mrb_value mesg) |
23 | 1.17k | { |
24 | 1.17k | if (!mrb_string_p(mesg)) { |
25 | 0 | mesg = mrb_obj_as_string(mrb, mesg); |
26 | 0 | } |
27 | 1.17k | exc->mesg = mrb_obj_ptr(mesg); |
28 | 1.17k | mrb_field_write_barrier_value(mrb, (struct RBasic*)exc, mesg); |
29 | 1.17k | } |
30 | | |
31 | | mrb_value |
32 | | mrb_exc_mesg_get(mrb_state *mrb, struct RException *exc) |
33 | 0 | { |
34 | 0 | if (exc->mesg == NULL) return mrb_nil_value(); |
35 | 0 | return mrb_obj_value(exc->mesg); |
36 | 0 | } |
37 | | |
38 | | MRB_API mrb_value |
39 | | mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str) |
40 | 1.16k | { |
41 | 1.16k | mrb_ensure_string_type(mrb, str); |
42 | | |
43 | 1.16k | struct RException *e = MRB_OBJ_ALLOC(mrb, MRB_TT_EXCEPTION, c); |
44 | 1.16k | mrb_value exc = mrb_obj_value(e); |
45 | 1.16k | mrb_exc_mesg_set(mrb, e, str); |
46 | 1.16k | return exc; |
47 | 1.16k | } |
48 | | |
49 | | MRB_API mrb_value |
50 | | mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, mrb_int len) |
51 | 0 | { |
52 | 0 | return mrb_exc_new_str(mrb, c, mrb_str_new(mrb, ptr, len)); |
53 | 0 | } |
54 | | |
55 | | /* |
56 | | * call-seq: |
57 | | * Exception.new(msg = nil) -> exception |
58 | | * |
59 | | * Construct a new Exception object, optionally passing in |
60 | | * a message. |
61 | | */ |
62 | | |
63 | | static mrb_value |
64 | | exc_initialize(mrb_state *mrb, mrb_value exc) |
65 | 16 | { |
66 | 16 | mrb_value mesg; |
67 | | |
68 | 16 | if (mrb_get_args(mrb, "|o", &mesg) == 1) { |
69 | 16 | mrb_exc_mesg_set(mrb, mrb_exc_ptr(exc), mesg); |
70 | 16 | } |
71 | 16 | return exc; |
72 | 16 | } |
73 | | |
74 | | /* |
75 | | * Document-method: exception |
76 | | * |
77 | | * call-seq: |
78 | | * exc.exception(string) -> an_exception or exc |
79 | | * |
80 | | * With no argument, or if the argument is the same as the receiver, |
81 | | * return the receiver. Otherwise, create a new |
82 | | * exception object of the same class as the receiver, but with a |
83 | | * message equal to <code>string</code>. |
84 | | * |
85 | | */ |
86 | | |
87 | | static mrb_value |
88 | | exc_exception(mrb_state *mrb, mrb_value self) |
89 | 0 | { |
90 | 0 | mrb_value exc; |
91 | 0 | mrb_value a; |
92 | 0 | mrb_int argc; |
93 | |
|
94 | 0 | argc = mrb_get_args(mrb, "|o", &a); |
95 | 0 | if (argc == 0) return self; |
96 | 0 | if (mrb_obj_equal(mrb, self, a)) return self; |
97 | 0 | exc = mrb_obj_clone(mrb, self); |
98 | 0 | mrb_exc_mesg_set(mrb, mrb_exc_ptr(exc), a); |
99 | |
|
100 | 0 | return exc; |
101 | 0 | } |
102 | | |
103 | | /* |
104 | | * call-seq: |
105 | | * exception.to_s -> string |
106 | | * |
107 | | * Returns exception's message (or the name of the exception if |
108 | | * no message is set). |
109 | | */ |
110 | | |
111 | | static mrb_value |
112 | | exc_to_s(mrb_state *mrb, mrb_value exc) |
113 | 0 | { |
114 | 0 | mrb_value mesg = mrb_exc_mesg_get(mrb, mrb_exc_ptr(exc)); |
115 | 0 | struct RObject *p; |
116 | |
|
117 | 0 | if (!mrb_string_p(mesg)) { |
118 | 0 | return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, exc)); |
119 | 0 | } |
120 | 0 | p = mrb_obj_ptr(mesg); |
121 | 0 | if (!p->c) { |
122 | 0 | p->c = mrb->string_class; |
123 | 0 | } |
124 | 0 | return mesg; |
125 | 0 | } |
126 | | |
127 | | /* |
128 | | * call-seq: |
129 | | * exception.inspect -> string |
130 | | * |
131 | | * Returns this exception's file name, line number, |
132 | | * message and class name. |
133 | | * If file name or line number is not set, |
134 | | * returns message and class name. |
135 | | */ |
136 | | |
137 | | mrb_value |
138 | | mrb_exc_inspect(mrb_state *mrb, mrb_value exc) |
139 | 0 | { |
140 | 0 | mrb_value cname = mrb_mod_to_s(mrb, mrb_obj_value(mrb_obj_class(mrb, exc))); |
141 | 0 | mrb_value mesg = mrb_exc_mesg_get(mrb, mrb_exc_ptr(exc)); /* string or nil */ |
142 | 0 | return (mrb_nil_p(mesg)||RSTRING_LEN(mesg)==0) ? cname : mrb_format(mrb, "%v (%v)", mesg, cname); |
143 | 0 | } |
144 | | |
145 | | void mrb_keep_backtrace(mrb_state *mrb, mrb_value exc); |
146 | | |
147 | | static void |
148 | | set_backtrace(mrb_state *mrb, mrb_value exc, mrb_value backtrace) |
149 | 0 | { |
150 | 0 | if (!mrb_array_p(backtrace)) { |
151 | 0 | type_err: |
152 | 0 | mrb_raise(mrb, E_TYPE_ERROR, "backtrace must be Array of String"); |
153 | 0 | } |
154 | 0 | else { |
155 | 0 | const mrb_value *p = RARRAY_PTR(backtrace); |
156 | 0 | const mrb_value *pend = p + RARRAY_LEN(backtrace); |
157 | |
|
158 | 0 | while (p < pend) { |
159 | 0 | if (!mrb_string_p(*p)) goto type_err; |
160 | 0 | p++; |
161 | 0 | } |
162 | 0 | } |
163 | 0 | mrb_exc_ptr(exc)->backtrace = mrb_obj_ptr(backtrace); |
164 | 0 | mrb_field_write_barrier_value(mrb, mrb_basic_ptr(exc), backtrace); |
165 | 0 | } |
166 | | |
167 | | static mrb_value |
168 | | exc_set_backtrace(mrb_state *mrb, mrb_value exc) |
169 | 0 | { |
170 | 0 | mrb_value backtrace = mrb_get_arg1(mrb); |
171 | |
|
172 | 0 | set_backtrace(mrb, exc, backtrace); |
173 | 0 | return backtrace; |
174 | 0 | } |
175 | | |
176 | | void |
177 | | mrb_exc_set(mrb_state *mrb, mrb_value exc) |
178 | 148 | { |
179 | 148 | if (mrb_nil_p(exc)) { |
180 | 0 | mrb->exc = 0; |
181 | 0 | } |
182 | 148 | else { |
183 | 148 | mrb->exc = mrb_obj_ptr(exc); |
184 | 148 | if (mrb->gc.arena_idx > 0 && |
185 | 148 | (struct RBasic*)mrb->exc == mrb->gc.arena[mrb->gc.arena_idx-1]) { |
186 | 145 | mrb->gc.arena_idx--; |
187 | 145 | } |
188 | 148 | if (!mrb->gc.out_of_memory && !mrb_frozen_p(mrb->exc)) { |
189 | 145 | mrb_keep_backtrace(mrb, exc); |
190 | 145 | } |
191 | 148 | } |
192 | 148 | } |
193 | | |
194 | | static mrb_noreturn void |
195 | | exc_throw(mrb_state *mrb, mrb_value exc) |
196 | 137 | { |
197 | 137 | if (!mrb->jmp) { |
198 | 0 | mrb_print_error(mrb); |
199 | 0 | abort(); |
200 | 0 | } |
201 | 137 | MRB_THROW(mrb->jmp); |
202 | 0 | } |
203 | | |
204 | | MRB_API mrb_noreturn void |
205 | | mrb_exc_raise(mrb_state *mrb, mrb_value exc) |
206 | 137 | { |
207 | 137 | if (mrb_break_p(exc)) { |
208 | 0 | mrb->exc = mrb_obj_ptr(exc); |
209 | 0 | } |
210 | 137 | else { |
211 | 137 | if (mrb_type(exc) != MRB_TT_EXCEPTION) { |
212 | 0 | mrb_raise(mrb, E_TYPE_ERROR, "exception object expected"); |
213 | 0 | } |
214 | 137 | mrb_exc_set(mrb, exc); |
215 | 137 | } |
216 | 137 | exc_throw(mrb, exc); |
217 | 137 | } |
218 | | |
219 | | MRB_API mrb_noreturn void |
220 | | mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg) |
221 | 27 | { |
222 | 27 | mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mrb_str_new_cstr(mrb, msg))); |
223 | 27 | } |
224 | | |
225 | | /* |
226 | | * <code>vsprintf</code> like formatting. |
227 | | * |
228 | | * The syntax of a format sequence is as follows. |
229 | | * |
230 | | * %[modifier]specifier |
231 | | * |
232 | | * The modifiers are: |
233 | | * |
234 | | * ----------+------------------------------------------------------------ |
235 | | * Modifier | Meaning |
236 | | * ----------+------------------------------------------------------------ |
237 | | * ! | Convert to string by corresponding `inspect` instead of |
238 | | * | corresponding `to_s`. |
239 | | * ----------+------------------------------------------------------------ |
240 | | * |
241 | | * The specifiers are: |
242 | | * |
243 | | * ----------+----------------+-------------------------------------------- |
244 | | * Specifier | Argument Type | Note |
245 | | * ----------+----------------+-------------------------------------------- |
246 | | * c | char | |
247 | | * d | int | |
248 | | * f | mrb_float | |
249 | | * i | mrb_int | |
250 | | * l | char*, size_t | Arguments are string and length. |
251 | | * n | mrb_sym | |
252 | | * s | char* | Argument is NUL terminated string. |
253 | | * t | mrb_value | Convert to type (class) of object. |
254 | | * v,S | mrb_value | |
255 | | * C | struct RClass* | |
256 | | * T | mrb_value | Convert to real type (class) of object. |
257 | | * Y | mrb_value | Same as `!v` if argument is `true`, `false` |
258 | | * | | or `nil`, otherwise same as `T`. |
259 | | * % | - | Convert to percent sign itself (no argument |
260 | | * | | taken). |
261 | | * ----------+----------------+-------------------------------------------- |
262 | | */ |
263 | | MRB_API mrb_value |
264 | | mrb_vformat(mrb_state *mrb, const char *format, va_list ap) |
265 | 102 | { |
266 | 102 | const char *chars, *p = format, *b = format, *e; |
267 | 102 | char ch; |
268 | 102 | size_t len; |
269 | 102 | mrb_int i; |
270 | 102 | struct RClass *cls; |
271 | 102 | mrb_bool inspect = FALSE; |
272 | 102 | mrb_value result = mrb_str_new_capa(mrb, 128), obj, str; |
273 | 102 | int ai = mrb_gc_arena_save(mrb); |
274 | | |
275 | 2.98k | while (*p) { |
276 | 2.88k | const char c = *p++; |
277 | 2.88k | e = p; |
278 | 2.88k | if (c == '%') { |
279 | 125 | if (*p == '!') { |
280 | 0 | inspect = TRUE; |
281 | 0 | p++; |
282 | 0 | } |
283 | 125 | if (!*p) break; |
284 | 125 | switch (*p) { |
285 | 0 | case 'c': |
286 | 0 | ch = (char)va_arg(ap, int); |
287 | 0 | chars = &ch; |
288 | 0 | len = 1; |
289 | 0 | goto L_cat; |
290 | 28 | case 'd': case 'i': |
291 | | #if MRB_INT_MAX < INT_MAX |
292 | | i = (mrb_int)va_arg(ap, int); |
293 | | #else |
294 | 28 | i = *p == 'd' ? (mrb_int)va_arg(ap, int) : va_arg(ap, mrb_int); |
295 | 28 | #endif |
296 | 28 | obj = mrb_int_value(mrb, i); |
297 | 28 | goto L_cat_obj; |
298 | 0 | #ifndef MRB_NO_FLOAT |
299 | 0 | case 'f': |
300 | 0 | obj = mrb_float_value(mrb, (mrb_float)va_arg(ap, double)); |
301 | 0 | goto L_cat_obj; |
302 | 0 | #endif |
303 | 0 | case 'l': |
304 | 0 | chars = va_arg(ap, char*); |
305 | 0 | len = va_arg(ap, size_t); |
306 | 5 | L_cat: |
307 | 5 | if (inspect) { |
308 | 0 | obj = mrb_str_new(mrb, chars, len); |
309 | 0 | goto L_cat_obj; |
310 | 0 | } |
311 | 125 | L_cat_plain: |
312 | 125 | mrb_str_cat(mrb, result, b, e - b - 1); |
313 | 125 | mrb_str_cat(mrb, result, chars, len); |
314 | 125 | b = ++p; |
315 | 125 | mrb_gc_arena_restore(mrb, ai); |
316 | 125 | break; |
317 | 37 | case 'n': |
318 | | #if UINT32_MAX < INT_MAX |
319 | | obj = mrb_symbol_value((mrb_sym)va_arg(ap, int)); |
320 | | #else |
321 | 37 | obj = mrb_symbol_value(va_arg(ap, mrb_sym)); |
322 | 37 | #endif |
323 | 37 | goto L_cat_obj; |
324 | 5 | case 's': |
325 | 5 | chars = va_arg(ap, char*); |
326 | 5 | len = strlen(chars); |
327 | 5 | goto L_cat; |
328 | 18 | case 't': |
329 | 18 | cls = mrb_class(mrb, va_arg(ap, mrb_value)); |
330 | 18 | goto L_cat_class; |
331 | 0 | case 'v': case 'S': |
332 | 0 | obj = va_arg(ap, mrb_value); |
333 | 120 | L_cat_obj: |
334 | 120 | str = (inspect ? mrb_inspect : mrb_obj_as_string)(mrb, obj); |
335 | 120 | if (mrb_type(str) != MRB_TT_STRING) { |
336 | 0 | chars = "void (no string conversion)"; |
337 | 0 | len = strlen(chars); |
338 | 0 | } |
339 | 120 | else { |
340 | 120 | chars = RSTRING_PTR(str); |
341 | 120 | len = RSTRING_LEN(str); |
342 | 120 | } |
343 | 120 | goto L_cat_plain; |
344 | 0 | case 'C': |
345 | 0 | cls = va_arg(ap, struct RClass*); |
346 | 18 | L_cat_class: |
347 | 18 | obj = mrb_obj_value(cls); |
348 | 18 | goto L_cat_obj; |
349 | 0 | case 'T': |
350 | 0 | obj = va_arg(ap, mrb_value); |
351 | 0 | L_cat_real_class_of: |
352 | 0 | cls = mrb_obj_class(mrb, obj); |
353 | 0 | goto L_cat_class; |
354 | 37 | case 'Y': |
355 | 37 | obj = va_arg(ap, mrb_value); |
356 | 37 | if (!mrb_test(obj) || mrb_true_p(obj)) { |
357 | 37 | inspect = TRUE; |
358 | 37 | goto L_cat_obj; |
359 | 37 | } |
360 | 0 | else { |
361 | 0 | goto L_cat_real_class_of; |
362 | 0 | } |
363 | 0 | case '%': |
364 | 0 | L_cat_current: |
365 | 0 | chars = p; |
366 | 0 | len = 1; |
367 | 0 | goto L_cat_plain; |
368 | 0 | default: |
369 | 0 | mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - %%%c", *p); |
370 | 125 | } |
371 | 125 | } |
372 | 2.75k | else if (c == '\\') { |
373 | 0 | if (!*p) break; |
374 | 0 | goto L_cat_current; |
375 | |
|
376 | 0 | } |
377 | 2.88k | } |
378 | | |
379 | 102 | mrb_str_cat(mrb, result, b, p - b); |
380 | 102 | return result; |
381 | 102 | } |
382 | | |
383 | | MRB_API mrb_value |
384 | | mrb_format(mrb_state *mrb, const char *format, ...) |
385 | 11 | { |
386 | 11 | va_list ap; |
387 | 11 | mrb_value str; |
388 | | |
389 | 11 | va_start(ap, format); |
390 | 11 | str = mrb_vformat(mrb, format, ap); |
391 | 11 | va_end(ap); |
392 | | |
393 | 11 | return str; |
394 | 11 | } |
395 | | |
396 | | static mrb_value |
397 | | error_va(mrb_state *mrb, struct RClass *c, const char *fmt, va_list ap) |
398 | 91 | { |
399 | 91 | mrb_value mesg = mrb_vformat(mrb, fmt, ap); |
400 | 91 | return mrb_exc_new_str(mrb, c, mesg); |
401 | 91 | } |
402 | | |
403 | | MRB_API mrb_noreturn void |
404 | | mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...) |
405 | 54 | { |
406 | 54 | va_list ap; |
407 | 54 | mrb_value exc; |
408 | | |
409 | 54 | va_start(ap, fmt); |
410 | 54 | exc = error_va(mrb, c, fmt, ap); |
411 | 54 | va_end(ap); |
412 | | |
413 | 54 | mrb_exc_raise(mrb, exc); |
414 | 54 | } |
415 | | |
416 | | MRB_API mrb_noreturn void |
417 | | mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...) |
418 | 0 | { |
419 | 0 | va_list ap; |
420 | 0 | mrb_value exc; |
421 | |
|
422 | 0 | va_start(ap, fmt); |
423 | 0 | exc = error_va(mrb, E_NAME_ERROR, fmt, ap); |
424 | 0 | va_end(ap); |
425 | 0 | mrb_iv_set(mrb, exc, MRB_IVSYM(name), mrb_symbol_value(id)); |
426 | 0 | mrb_exc_raise(mrb, exc); |
427 | 0 | } |
428 | | |
429 | | MRB_API void |
430 | | mrb_warn(mrb_state *mrb, const char *fmt, ...) |
431 | 0 | { |
432 | 0 | #ifndef MRB_NO_STDIO |
433 | 0 | va_list ap; |
434 | 0 | mrb_value str; |
435 | |
|
436 | 0 | va_start(ap, fmt); |
437 | 0 | str = mrb_vformat(mrb, fmt, ap); |
438 | 0 | fputs("warning: ", stderr); |
439 | 0 | fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr); |
440 | 0 | putc('\n', stderr); |
441 | 0 | va_end(ap); |
442 | 0 | #endif |
443 | 0 | } |
444 | | |
445 | | MRB_API mrb_noreturn void |
446 | | mrb_bug(mrb_state *mrb, const char *mesg) |
447 | 0 | { |
448 | 0 | #ifndef MRB_NO_STDIO |
449 | 0 | fputs("bug: ", stderr); |
450 | 0 | fputs(mesg, stderr); |
451 | 0 | fputs("\n", stderr); |
452 | 0 | #endif |
453 | 0 | exit(EXIT_FAILURE); |
454 | 0 | } |
455 | | |
456 | | mrb_value |
457 | | mrb_make_exception(mrb_state *mrb, mrb_value exc, mrb_value mesg) |
458 | 16 | { |
459 | 16 | mrb_int n = 1; |
460 | | |
461 | 16 | if (mrb_nil_p(mesg)) { |
462 | 0 | n = 0; |
463 | 0 | } |
464 | 16 | if (mrb_class_p(exc)) { |
465 | 16 | exc = mrb_funcall_argv(mrb, exc, MRB_SYM(new), n, &mesg); |
466 | 16 | } |
467 | 0 | else if (mrb_exception_p(exc)) { |
468 | 0 | if (n > 0) { |
469 | 0 | exc = mrb_obj_clone(mrb, exc); |
470 | 0 | mrb_exc_mesg_set(mrb, mrb_exc_ptr(exc), mesg); |
471 | 0 | } |
472 | 0 | } |
473 | 0 | else { |
474 | 0 | mrb_raise(mrb, E_TYPE_ERROR, "exception class/object expected"); |
475 | 0 | } |
476 | 16 | if (mrb_type(exc) != MRB_TT_EXCEPTION) { |
477 | 0 | mrb_raise(mrb, E_EXCEPTION, "exception object expected"); |
478 | 0 | } |
479 | 16 | return exc; |
480 | 16 | } |
481 | | |
482 | | MRB_API mrb_noreturn void |
483 | | mrb_sys_fail(mrb_state *mrb, const char *mesg) |
484 | 0 | { |
485 | 0 | struct RClass *sce; |
486 | 0 | mrb_int no; |
487 | |
|
488 | 0 | no = (mrb_int)errno; |
489 | 0 | if (mrb_class_defined_id(mrb, MRB_SYM(SystemCallError))) { |
490 | 0 | sce = mrb_class_get_id(mrb, MRB_SYM(SystemCallError)); |
491 | 0 | if (mesg != NULL) { |
492 | 0 | mrb_funcall_id(mrb, mrb_obj_value(sce), MRB_SYM(_sys_fail), 2, mrb_fixnum_value(no), mrb_str_new_cstr(mrb, mesg)); |
493 | 0 | } |
494 | 0 | else { |
495 | 0 | mrb_funcall_id(mrb, mrb_obj_value(sce), MRB_SYM(_sys_fail), 1, mrb_fixnum_value(no)); |
496 | 0 | } |
497 | 0 | } |
498 | |
|
499 | 0 | mrb_raise(mrb, E_RUNTIME_ERROR, mesg); |
500 | 0 | } |
501 | | |
502 | | MRB_API mrb_noreturn void |
503 | | mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, ...) |
504 | 37 | { |
505 | 37 | va_list ap; |
506 | 37 | mrb_value exc; |
507 | | |
508 | 37 | va_start(ap, fmt); |
509 | 37 | exc = error_va(mrb, E_NOMETHOD_ERROR, fmt, ap); |
510 | 37 | va_end(ap); |
511 | 37 | mrb_iv_set(mrb, exc, MRB_IVSYM(name), mrb_symbol_value(id)); |
512 | 37 | mrb_iv_set(mrb, exc, MRB_IVSYM(args), args); |
513 | 37 | mrb_exc_raise(mrb, exc); |
514 | 37 | } |
515 | | |
516 | | static mrb_noreturn void |
517 | | frozen_error(mrb_state *mrb, mrb_value v) |
518 | 0 | { |
519 | 0 | mrb_raisef(mrb, E_FROZEN_ERROR, "can't modify frozen %T", v); |
520 | 0 | } |
521 | | |
522 | | MRB_API mrb_noreturn void |
523 | | mrb_frozen_error(mrb_state *mrb, void *frozen_obj) |
524 | 0 | { |
525 | 0 | frozen_error(mrb, mrb_obj_value(frozen_obj)); |
526 | 0 | } |
527 | | |
528 | | MRB_API void |
529 | | mrb_check_frozen(mrb_state *mrb, void *o) |
530 | 1.46M | { |
531 | 1.46M | if (mrb_frozen_p((struct RBasic*)o)) { |
532 | 0 | mrb_frozen_error(mrb, o); |
533 | 0 | } |
534 | 1.46M | } |
535 | | |
536 | | MRB_API void |
537 | | mrb_check_frozen_value(mrb_state *mrb, mrb_value v) |
538 | 0 | { |
539 | 0 | if (mrb_immediate_p(v) || mrb_frozen_p(mrb_basic_ptr(v))) { |
540 | 0 | frozen_error(mrb, v); |
541 | 0 | } |
542 | 0 | } |
543 | | |
544 | | MRB_API mrb_noreturn void |
545 | | mrb_argnum_error(mrb_state *mrb, mrb_int argc, int min, int max) |
546 | 3 | { |
547 | 3 | #define FMT(exp) "wrong number of arguments (given %i, expected " exp ")" |
548 | 3 | if (min == max) |
549 | 3 | mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d"), argc, min); |
550 | 0 | else if (max < 0) |
551 | 0 | mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d+"), argc, min); |
552 | 0 | else |
553 | 0 | mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d..%d"), argc, min, max); |
554 | 3 | #undef FMT |
555 | 3 | } |
556 | | |
557 | | void mrb_core_init_printabort(void); |
558 | | |
559 | | int |
560 | | mrb_core_init_protect(mrb_state *mrb, void (*body)(mrb_state*, void*), void *opaque) |
561 | 952 | { |
562 | 952 | struct mrb_jmpbuf *prev_jmp = mrb->jmp; |
563 | 952 | struct mrb_jmpbuf c_jmp; |
564 | 952 | int err = 1; |
565 | | |
566 | 952 | MRB_TRY(&c_jmp) { |
567 | 952 | mrb->jmp = &c_jmp; |
568 | 952 | body(mrb, opaque); |
569 | 952 | err = 0; |
570 | 952 | } MRB_CATCH(&c_jmp) { |
571 | 0 | if (mrb->exc) { |
572 | 0 | mrb_print_error(mrb); |
573 | 0 | mrb->exc = NULL; |
574 | 0 | } |
575 | 0 | else { |
576 | 0 | mrb_core_init_printabort(); |
577 | 0 | } |
578 | 0 | } MRB_END_EXC(&c_jmp); |
579 | | |
580 | 952 | mrb->jmp = prev_jmp; |
581 | | |
582 | 952 | return err; |
583 | 952 | } |
584 | | |
585 | | mrb_noreturn void |
586 | | mrb_core_init_abort(mrb_state *mrb) |
587 | 0 | { |
588 | 0 | mrb->exc = NULL; |
589 | 0 | exc_throw(mrb, mrb_nil_value()); |
590 | 0 | } |
591 | | |
592 | | void |
593 | | mrb_protect_atexit(mrb_state *mrb) |
594 | 476 | { |
595 | 476 | if (mrb->atexit_stack_len > 0) { |
596 | 476 | struct mrb_jmpbuf *prev_jmp = mrb->jmp; |
597 | 476 | struct mrb_jmpbuf c_jmp; |
598 | 952 | for (int i = mrb->atexit_stack_len; i > 0; --i) { |
599 | 476 | MRB_TRY(&c_jmp) { |
600 | 476 | mrb->jmp = &c_jmp; |
601 | 476 | mrb->atexit_stack[i - 1](mrb); |
602 | 476 | mrb->jmp = prev_jmp; |
603 | 476 | } MRB_CATCH(&c_jmp) { |
604 | | /* ignore atexit errors */ |
605 | 0 | } MRB_END_EXC(&c_jmp); |
606 | 476 | } |
607 | 476 | #ifndef MRB_FIXED_STATE_ATEXIT_STACK |
608 | 476 | mrb_free(mrb, mrb->atexit_stack); |
609 | 476 | #endif |
610 | 476 | mrb->jmp = prev_jmp; |
611 | 476 | } |
612 | 476 | } |
613 | | |
614 | | mrb_noreturn void |
615 | | mrb_raise_nomemory(mrb_state *mrb) |
616 | 3 | { |
617 | 3 | if (mrb->nomem_err) { |
618 | 3 | mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err)); |
619 | 3 | } |
620 | 0 | else { |
621 | 0 | mrb_core_init_abort(mrb); |
622 | 0 | } |
623 | 3 | } |
624 | | |
625 | | MRB_API void |
626 | | mrb_print_error(mrb_state *mrb) |
627 | 0 | { |
628 | 0 | #ifndef MRB_NO_STDIO |
629 | 0 | if (mrb->jmp == NULL) { |
630 | 0 | struct mrb_jmpbuf c_jmp; |
631 | 0 | MRB_TRY(&c_jmp) { |
632 | 0 | mrb->jmp = &c_jmp; |
633 | 0 | mrb_print_backtrace(mrb); |
634 | 0 | } MRB_CATCH(&c_jmp) { |
635 | | /* ignore exception during print_backtrace() */ |
636 | 0 | } MRB_END_EXC(&c_jmp); |
637 | 0 | mrb->jmp = NULL; |
638 | 0 | } |
639 | 0 | else { |
640 | 0 | mrb_print_backtrace(mrb); |
641 | 0 | } |
642 | 0 | #endif |
643 | 0 | } |
644 | | |
645 | | /* clear error status in the mrb_state structure */ |
646 | | MRB_API void |
647 | | mrb_clear_error(mrb_state *mrb) |
648 | 0 | { |
649 | 0 | mrb->exc = NULL; |
650 | 0 | } |
651 | | |
652 | | /* returns TRUE if error in the previous call; internally calls mrb_clear_error() */ |
653 | | MRB_API mrb_bool |
654 | | mrb_check_error(mrb_state *mrb) |
655 | 0 | { |
656 | 0 | if (mrb->exc) { |
657 | 0 | mrb_clear_error(mrb); |
658 | 0 | return TRUE; |
659 | 0 | } |
660 | 0 | return FALSE; |
661 | 0 | } |
662 | | |
663 | | void |
664 | | mrb_init_exception(mrb_state *mrb) |
665 | 476 | { |
666 | 476 | struct RClass *exception, *script_error, *stack_error, *nomem_error; |
667 | | |
668 | 476 | mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */ |
669 | 476 | MRB_SET_INSTANCE_TT(exception, MRB_TT_EXCEPTION); |
670 | 476 | mrb_define_class_method(mrb, exception, "exception", mrb_instance_new, MRB_ARGS_OPT(1)); |
671 | 476 | mrb_define_method(mrb, exception, "exception", exc_exception, MRB_ARGS_OPT(1)); |
672 | 476 | mrb_define_method(mrb, exception, "initialize", exc_initialize, MRB_ARGS_OPT(1)); |
673 | 476 | mrb_define_method(mrb, exception, "to_s", exc_to_s, MRB_ARGS_NONE()); |
674 | 476 | mrb_define_method(mrb, exception, "inspect", mrb_exc_inspect, MRB_ARGS_NONE()); |
675 | 476 | mrb_define_method(mrb, exception, "backtrace", mrb_exc_backtrace, MRB_ARGS_NONE()); |
676 | 476 | mrb_define_method(mrb, exception, "set_backtrace", exc_set_backtrace, MRB_ARGS_REQ(1)); |
677 | | |
678 | 476 | mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */ |
679 | 476 | mrb_define_class(mrb, "RuntimeError", E_STANDARD_ERROR); /* 15.2.28 */ |
680 | 476 | script_error = mrb_define_class(mrb, "ScriptError", exception); /* 15.2.37 */ |
681 | 476 | mrb_define_class(mrb, "SyntaxError", script_error); /* 15.2.38 */ |
682 | 476 | stack_error = mrb_define_class(mrb, "SystemStackError", exception); |
683 | 476 | mrb->stack_err = mrb_obj_ptr(mrb_exc_new_lit(mrb, stack_error, "stack level too deep")); |
684 | | |
685 | 476 | nomem_error = mrb_define_class(mrb, "NoMemoryError", exception); |
686 | 476 | mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_lit(mrb, nomem_error, "Out of memory")); |
687 | | #ifdef MRB_GC_FIXED_ARENA |
688 | | mrb->arena_err = mrb_obj_ptr(mrb_exc_new_lit(mrb, nomem_error, "arena overflow error")); |
689 | | #endif |
690 | 476 | } |