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                             &registers);
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