1 /* Copyright 1995-1998,2000-2001,2003-2004,2006,2008,2009-2014,2017-2019
2 Free Software Foundation, Inc.
3
4 This file is part of Guile.
5
6 Guile is free software: you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Guile is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with Guile. If not, see
18 <https://www.gnu.org/licenses/>. */
19
20
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #include <alloca.h>
27 #include <stdio.h>
28 #include <unistdio.h>
29
30 #include "boolean.h"
31 #include "control.h"
32 #include "eq.h"
33 #include "eval.h"
34 #include "fluids.h"
35 #include "gsubr.h"
36 #include "init.h"
37 #include "keywords.h"
38 #include "list.h"
39 #include "modules.h"
40 #include "numbers.h"
41 #include "pairs.h"
42 #include "ports.h"
43 #include "smob.h"
44 #include "stackchk.h"
45 #include "stacks.h"
46 #include "strings.h"
47 #include "symbols.h"
48 #include "variable.h"
49
50 #include "exceptions.h"
51
52
53 /* Pleasantly enough, the guts of exception handling are defined in
54 Scheme, in terms of prompt, abort, and the %exception-handler fluid.
55 Check boot-9 for the definitions.
56
57 Still, it's useful to be able to raise unwind-only exceptions from C,
58 for example so that we can recover from stack overflow. We also need
59 to have implementations of with-exception-handler and raise handy
60 before boot time. For that reason we have a parallel implementation
61 of with-exception-handler that uses the same fluids here. Exceptions
62 raised from C still call out to Scheme though, so that pre-unwind
63 handlers can be run. */
64
65
66
67
68 /* First, some support for C bodies and exception handlers. */
69
70 static scm_t_bits tc16_thunk;
71 static scm_t_bits tc16_exception_handler;
72
73 SCM
scm_c_make_thunk(scm_t_thunk thunk,void * data)74 scm_c_make_thunk (scm_t_thunk thunk, void *data)
75 {
76 SCM_RETURN_NEWSMOB2 (tc16_thunk, thunk, data);
77 }
78
79 SCM
scm_c_make_exception_handler(scm_t_exception_handler handler,void * data)80 scm_c_make_exception_handler (scm_t_exception_handler handler, void *data)
81 {
82 SCM_RETURN_NEWSMOB2 (tc16_exception_handler, handler, data);
83 }
84
85 static SCM
call_thunk(SCM clo)86 call_thunk (SCM clo)
87 {
88 scm_t_thunk thunk = (void*)SCM_SMOB_DATA (clo);
89 void *data = (void*)SCM_SMOB_DATA_2 (clo);
90
91 return thunk (data);
92 }
93
94 static SCM
call_exception_handler(SCM clo,SCM exn)95 call_exception_handler (SCM clo, SCM exn)
96 {
97 scm_t_exception_handler handler = (void*)SCM_SMOB_DATA (clo);
98 void *data = (void*)SCM_SMOB_DATA_2 (clo);
99
100 return handler (data, exn);
101 }
102
103
104
105
106 /* Now, the implementation of with-exception-handler used internally to
107 Guile at boot-time. */
108
109 SCM_KEYWORD (kw_unwind_p, "unwind?");
110 SCM_KEYWORD (kw_unwind_for_type, "unwind-for-type");
111 static SCM exception_handler_fluid;
112 static SCM active_exception_handlers_fluid;
113 static SCM with_exception_handler_var;
114 static SCM raise_exception_var;
115
116 SCM
scm_c_with_exception_handler(SCM type,scm_t_exception_handler handler,void * handler_data,scm_t_thunk thunk,void * thunk_data)117 scm_c_with_exception_handler (SCM type, scm_t_exception_handler handler,
118 void *handler_data,
119 scm_t_thunk thunk, void *thunk_data)
120 {
121 if (!scm_is_eq (type, SCM_BOOL_T) && !scm_is_symbol (type))
122 scm_wrong_type_arg ("%with-exception-handler", 1, type);
123
124 SCM prompt_tag = scm_cons (SCM_INUM0, SCM_EOL);
125 scm_thread *t = SCM_I_CURRENT_THREAD;
126 scm_t_dynstack *dynstack = &t->dynstack;
127 scm_t_dynamic_state *dynamic_state = t->dynamic_state;
128 jmp_buf registers;
129 jmp_buf *prev_registers;
130 ptrdiff_t saved_stack_depth;
131 uint8_t *mra = NULL;
132
133 prev_registers = t->vm.registers;
134 saved_stack_depth = t->vm.stack_top - t->vm.sp;
135
136 /* Push the prompt and exception handler onto the dynamic stack. */
137 scm_dynstack_push_prompt (dynstack,
138 SCM_F_DYNSTACK_PROMPT_ESCAPE_ONLY,
139 prompt_tag,
140 t->vm.stack_top - t->vm.fp,
141 saved_stack_depth,
142 t->vm.ip,
143 mra,
144 ®isters);
145 scm_dynstack_push_fluid (dynstack, exception_handler_fluid,
146 scm_cons (prompt_tag, type),
147 dynamic_state);
148
149 if (setjmp (registers))
150 {
151 /* A non-local return. */
152 SCM args;
153
154 t->vm.registers = prev_registers;
155 scm_gc_after_nonlocal_exit ();
156
157 /* FIXME: We know where the args will be on the stack; we could
158 avoid consing them. */
159 args = scm_i_prompt_pop_abort_args_x (&t->vm, saved_stack_depth);
160
161 /* The first abort arg is the continuation, which is #f. The
162 second and final arg is the exception. */
163 args = scm_cdr (args);
164 SCM exn = scm_car (args);
165 if (!scm_is_null (scm_cdr (args)))
166 abort ();
167 return handler (handler_data, exn);
168 }
169
170 SCM res = thunk (thunk_data);
171
172 scm_dynstack_unwind_fluid (dynstack, dynamic_state);
173 scm_dynstack_pop (dynstack);
174
175 return res;
176 }
177
178 SCM
scm_with_exception_handler(SCM type,SCM handler,SCM thunk)179 scm_with_exception_handler (SCM type, SCM handler, SCM thunk)
180 {
181 return scm_call_6 (scm_variable_ref (with_exception_handler_var),
182 handler, thunk, kw_unwind_p, SCM_BOOL_T,
183 kw_unwind_for_type, type);
184 }
185
186 SCM
scm_with_pre_unwind_exception_handler(SCM handler,SCM thunk)187 scm_with_pre_unwind_exception_handler (SCM handler, SCM thunk)
188 {
189 return scm_call_2 (scm_variable_ref (with_exception_handler_var),
190 handler, thunk);
191 }
192
193
194
195
196 SCM_SYMBOL (sys_exception_sym, "%exception");
197 /* Note that these record types are marked as non-extensible, so their
198 type predicate is a simple vtable comparison. */
199 static SCM compound_exception;
200 static SCM exception_with_kind_and_args;
201 static SCM quit_exception;
202
203 static SCM
extract_exception(SCM obj,SCM non_extensible_vtable)204 extract_exception (SCM obj, SCM non_extensible_vtable)
205 {
206 if (!SCM_STRUCTP (obj)) {
207 return SCM_BOOL_F;
208 }
209 if (scm_is_eq (SCM_STRUCT_VTABLE (obj), non_extensible_vtable)) {
210 return obj;
211 }
212 if (!scm_is_eq (SCM_STRUCT_VTABLE (obj), compound_exception)) {
213 return SCM_BOOL_F;
214 }
215
216 SCM exns = SCM_STRUCT_SLOT_REF (obj, 0);
217 while (!scm_is_null (exns)) {
218 SCM exn = scm_car (exns);
219 if (scm_is_eq (SCM_STRUCT_VTABLE (exn), non_extensible_vtable)) {
220 return exn;
221 }
222 exns = scm_cdr (exns);
223 }
224 return SCM_BOOL_F;
225 }
226
227 SCM
scm_exception_kind(SCM obj)228 scm_exception_kind (SCM obj)
229 {
230 SCM exn = extract_exception (obj, exception_with_kind_and_args);
231 if (scm_is_false (exn)) {
232 return sys_exception_sym;
233 }
234 return SCM_STRUCT_SLOT_REF (exn, 0);
235 }
236
237 SCM
scm_exception_args(SCM obj)238 scm_exception_args (SCM obj)
239 {
240 SCM exn = extract_exception (obj, exception_with_kind_and_args);
241 if (scm_is_false (exn)) {
242 return scm_list_1 (obj);
243 }
244 return SCM_STRUCT_SLOT_REF (exn, 1);
245 }
246
247 static int
exception_has_type(SCM exn,SCM type)248 exception_has_type (SCM exn, SCM type)
249 {
250 return scm_is_eq (type, SCM_BOOL_T) ||
251 scm_is_eq (type, scm_exception_kind (exn));
252 }
253
254
255
256
257 void
scm_dynwind_throw_handler(void)258 scm_dynwind_throw_handler (void)
259 {
260 scm_dynwind_fluid (active_exception_handlers_fluid, SCM_BOOL_F);
261 }
262
263
264
265
266 /* Default exception handlers. */
267
268 /* Derive the an exit status from the arguments to (quit ...). */
269 int
scm_exit_status(SCM args)270 scm_exit_status (SCM args)
271 {
272 if (scm_is_pair (args))
273 {
274 SCM cqa = SCM_CAR (args);
275
276 if (scm_is_integer (cqa))
277 return scm_to_int (cqa);
278 else if (scm_is_false (cqa))
279 return EXIT_FAILURE;
280 else
281 return EXIT_SUCCESS;
282 }
283 else if (scm_is_null (args))
284 return EXIT_SUCCESS;
285 else
286 /* A type error. Strictly speaking we shouldn't get here. */
287 return EXIT_FAILURE;
288 }
289
290 static SCM
get_quit_exception(SCM obj)291 get_quit_exception (SCM obj)
292 {
293 return extract_exception (obj, quit_exception);
294 }
295
296 static int
quit_exception_code(SCM exn)297 quit_exception_code (SCM exn)
298 {
299 return scm_to_int (SCM_STRUCT_SLOT_REF (exn, 0));
300 }
301
302 static void
scm_display_exception(SCM port,SCM exn)303 scm_display_exception (SCM port, SCM exn)
304 {
305 // FIXME: Make a good exception printer.
306 scm_puts ("key: ", port);
307 scm_write (scm_exception_kind (exn), port);
308 scm_puts (", args: ", port);
309 scm_write (scm_exception_args (exn), port);
310 scm_newline (port);
311 }
312
313 static void
default_exception_handler(SCM exn)314 default_exception_handler (SCM exn)
315 {
316 static int error_printing_error = 0;
317 static int error_printing_fallback = 0;
318
319 if (error_printing_fallback)
320 fprintf (stderr, "\nFailed to print exception.\n");
321 else if (error_printing_error)
322 {
323 fprintf (stderr, "\nError while printing exception:\n");
324 error_printing_fallback = 1;
325 scm_write (exn, scm_current_error_port ());
326 scm_newline (scm_current_error_port ());
327 }
328 else if (scm_is_true (get_quit_exception (exn)))
329 {
330 exit (quit_exception_code (get_quit_exception (exn)));
331 }
332 else
333 {
334 SCM port = scm_current_error_port ();
335 error_printing_error = 1;
336 scm_puts ("Uncaught exception:\n", port);
337 scm_display_exception (port, exn);
338 scm_i_pthread_exit (NULL);
339 }
340
341 /* We fall through here for the error-printing-error cases. */
342 fprintf (stderr, "Aborting.\n");
343 abort ();
344 }
345
346 static SCM
default_exception_handler_wrapper(void * data,SCM exn)347 default_exception_handler_wrapper (void *data, SCM exn)
348 {
349 default_exception_handler (exn);
350 return SCM_UNDEFINED;
351 }
352
353 SCM
scm_c_with_default_exception_handler(scm_t_thunk thunk,void * data)354 scm_c_with_default_exception_handler (scm_t_thunk thunk, void *data)
355 {
356 return scm_c_with_exception_handler (SCM_BOOL_T,
357 default_exception_handler_wrapper, NULL,
358 thunk, data);
359 }
360
361
362
363
364 /* An implementation of "raise" for use during boot and in
365 resource-exhaustion situations. */
366
367
368
369 static void
emergency_raise(SCM exn,const char * reason)370 emergency_raise (SCM exn, const char *reason)
371 {
372 size_t depth = 0;
373
374 /* This function is not only the boot implementation of "raise", it is
375 also called in response to resource allocation failures such as
376 stack-overflow or out-of-memory. For that reason we need to be
377 careful to avoid allocating memory. */
378 while (1)
379 {
380 SCM eh = scm_fluid_ref_star (exception_handler_fluid,
381 scm_from_size_t (depth++));
382 if (scm_is_false (eh)) {
383 default_exception_handler (exn);
384 abort ();
385 }
386
387 if (!scm_is_pair (eh)) {
388 fprintf (stderr, "Warning: Unwind-only %s exception; "
389 "skipping pre-unwind handler.\n", reason);
390 } else {
391 SCM prompt_tag = scm_car (eh);
392 SCM type = scm_cdr (eh);
393 if (exception_has_type (exn, type)) {
394 SCM tag_and_exn[] = { prompt_tag, exn };
395 scm_i_vm_emergency_abort (tag_and_exn, 2);
396 /* Unreachable. */
397 abort ();
398 }
399 }
400 }
401 }
402
403 static SCM
pre_boot_raise(SCM exn)404 pre_boot_raise (SCM exn)
405 {
406 emergency_raise (exn, "pre-boot");
407 return SCM_UNDEFINED;
408 }
409
410 SCM
scm_raise_exception(SCM exn)411 scm_raise_exception (SCM exn)
412 {
413 scm_call_1 (scm_variable_ref (raise_exception_var), exn);
414 /* Should not be reached. */
415 abort ();
416 }
417
418
419
420
421 SCM_SYMBOL (scm_stack_overflow_key, "stack-overflow");
422 SCM_SYMBOL (scm_out_of_memory_key, "out-of-memory");
423
424 static SCM stack_overflow_exn = SCM_BOOL_F;
425 static SCM out_of_memory_exn = SCM_BOOL_F;
426
427 /* Since these two functions may be called in response to resource
428 exhaustion, we have to avoid allocating memory. */
429
430 void
scm_report_stack_overflow(void)431 scm_report_stack_overflow (void)
432 {
433 if (scm_is_false (stack_overflow_exn))
434 abort ();
435 emergency_raise (stack_overflow_exn, "stack overflow");
436
437 /* Not reached. */
438 abort ();
439 }
440
441 void
scm_report_out_of_memory(void)442 scm_report_out_of_memory (void)
443 {
444 if (scm_is_false (out_of_memory_exn))
445 abort ();
446 emergency_raise (out_of_memory_exn, "out of memory");
447
448 /* Not reached. */
449 abort ();
450 }
451
452 static SCM
make_scm_exception(SCM type,SCM subr,SCM message,SCM args,SCM rest)453 make_scm_exception (SCM type, SCM subr, SCM message, SCM args, SCM rest)
454 {
455 return scm_make_struct_simple
456 (exception_with_kind_and_args,
457 scm_list_2 (type,
458 scm_list_4 (subr, message, args, rest)));
459 }
460
461 static SCM
sys_init_exceptions_x(SCM compound_exception_type,SCM exception_with_kind_and_args_type,SCM quit_exception_type)462 sys_init_exceptions_x (SCM compound_exception_type,
463 SCM exception_with_kind_and_args_type,
464 SCM quit_exception_type)
465 {
466 compound_exception = compound_exception_type;
467 exception_with_kind_and_args = exception_with_kind_and_args_type;
468 quit_exception = quit_exception_type;
469
470
471 /* Arguments as if from:
472
473 scm_error (stack-overflow, NULL, "Stack overflow", #f, #f);
474
475 We build the arguments manually to avoid allocating memory in
476 emergency circumstances. */
477 stack_overflow_exn = make_scm_exception
478 (scm_stack_overflow_key, SCM_BOOL_F,
479 scm_from_latin1_string ("Stack overflow"), SCM_BOOL_F, SCM_BOOL_F);
480 out_of_memory_exn = make_scm_exception
481 (scm_out_of_memory_key, SCM_BOOL_F,
482 scm_from_latin1_string ("Out of memory"), SCM_BOOL_F, SCM_BOOL_F);
483
484 return SCM_UNDEFINED;
485 }
486
487
488
489
490 /* Initialization. */
491
492 void
scm_init_exceptions()493 scm_init_exceptions ()
494 {
495 tc16_thunk = scm_make_smob_type ("thunk", 0);
496 scm_set_smob_apply (tc16_thunk, call_thunk, 0, 0, 0);
497
498 tc16_exception_handler = scm_make_smob_type ("exception-handler", 0);
499 scm_set_smob_apply (tc16_exception_handler, call_exception_handler, 1, 0, 0);
500
501 exception_handler_fluid = scm_make_thread_local_fluid (SCM_BOOL_F);
502 active_exception_handlers_fluid = scm_make_thread_local_fluid (SCM_BOOL_F);
503 /* These binding are later removed when the Scheme definitions of
504 raise and with-exception-handler are created in boot-9.scm. */
505 scm_c_define ("%exception-handler", exception_handler_fluid);
506 scm_c_define ("%active-exception-handlers", active_exception_handlers_fluid);
507
508 with_exception_handler_var =
509 scm_c_define ("with-exception-handler", SCM_BOOL_F);
510 raise_exception_var =
511 scm_c_define ("raise-exception",
512 scm_c_make_gsubr ("raise-exception", 1, 0, 0,
513 (scm_t_subr) pre_boot_raise));
514
515 scm_c_define ("%init-exceptions!",
516 scm_c_make_gsubr ("%init-exceptions!", 3, 0, 0,
517 (scm_t_subr) sys_init_exceptions_x));
518
519 #include "exceptions.x"
520 }
521