* alloca.c: New file, needed to support the AC_FUNC_ALLOCA call in
[bpt/guile.git] / libguile / throw.c
CommitLineData
0f2d19dd
JB
1/* Copyright (C) 1995,1996 Free Software Foundation, Inc.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2, or (at your option)
6 * any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; see the file COPYING. If not, write to
15 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
16 *
17 * As a special exception, the Free Software Foundation gives permission
18 * for additional uses of the text contained in its release of GUILE.
19 *
20 * The exception is that, if you link the GUILE library with other files
21 * to produce an executable, this does not by itself cause the
22 * resulting executable to be covered by the GNU General Public License.
23 * Your use of that executable is in no way restricted on account of
24 * linking the GUILE library code into it.
25 *
26 * This exception does not however invalidate any other reasons why
27 * the executable file might be covered by the GNU General Public License.
28 *
29 * This exception applies only to the code released by the
30 * Free Software Foundation under the name GUILE. If you copy
31 * code from other Free Software Foundation releases into a copy of
32 * GUILE, as the General Public License permits, the exception does
33 * not apply to the code that you add in this way. To avoid misleading
34 * anyone as to the status of such modified files, you must delete
35 * this exception notice from them.
36 *
37 * If you write modifications of your own for GUILE, it is your choice
38 * whether to permit this exception to apply to your modifications.
39 * If you do not wish that, delete this exception notice.
40 */
41\f
42
43#include <stdio.h>
44#include "_scm.h"
20e6290e
JB
45#include "genio.h"
46#include "smob.h"
47#include "alist.h"
48#include "eval.h"
49#include "dynwind.h"
e82afdb8 50#include "backtrace.h"
20e6290e
JB
51#ifdef DEBUG_EXTENSIONS
52#include "debug.h"
53#endif
54#include "continuations.h"
7f759d79 55#include "stackchk.h"
0f2d19dd 56
20e6290e 57#include "throw.h"
0f2d19dd 58
32f7b3a1 59\f
0f2d19dd
JB
60/* {Catch and Throw}
61 */
62static int scm_tc16_jmpbuffer;
63
0f2d19dd
JB
64#define SCM_JMPBUFP(O) (SCM_TYP16(O) == scm_tc16_jmpbuffer)
65#define JBACTIVE(O) (SCM_CAR (O) & (1L << 16L))
898a256f
MD
66#define ACTIVATEJB(O) (SCM_SETOR_CAR (O, (1L << 16L)))
67#define DEACTIVATEJB(O) (SCM_SETAND_CAR (O, ~(1L << 16L)))
0f2d19dd 68
e137c6b3
MD
69#ifndef DEBUG_EXTENSIONS
70#define JBJMPBUF(O) ((jmp_buf*)SCM_CDR (O) )
71#define SETJBJMPBUF SCM_SETCDR
72#else
08b5b88c 73#define SCM_JBDFRAME(O) ((scm_debug_frame*)SCM_CAR (SCM_CDR (O)) )
0f2d19dd 74#define JBJMPBUF(O) ((jmp_buf*)SCM_CDR (SCM_CDR (O)) )
898a256f 75#define SCM_SETJBDFRAME(O,X) SCM_SETCAR (SCM_CDR (O), (SCM)(X))
0f2d19dd 76#define SETJBJMPBUF(O,X) SCM_SETCDR(SCM_CDR (O), X)
e137c6b3 77
32f7b3a1
JB
78static scm_sizet freejb SCM_P ((SCM jbsmob));
79
faa6b3df 80static scm_sizet
e137c6b3
MD
81freejb (jbsmob)
82 SCM jbsmob;
e137c6b3
MD
83{
84 scm_must_free ((char *) SCM_CDR (jbsmob));
85 return sizeof (scm_cell);
86}
0f2d19dd
JB
87#endif
88
32f7b3a1 89static int printjb SCM_P ((SCM exp, SCM port, scm_print_state *pstate));
0f2d19dd 90static int
9882ea19 91printjb (exp, port, pstate)
0f2d19dd
JB
92 SCM exp;
93 SCM port;
9882ea19 94 scm_print_state *pstate;
0f2d19dd
JB
95{
96 scm_gen_puts (scm_regular_string, "#<jmpbuffer ", port);
11702758 97 scm_gen_puts (scm_regular_string, JBACTIVE(exp) ? "(active) " : "(inactive) ", port);
0f2d19dd
JB
98 scm_intprint((SCM) JBJMPBUF(exp), 16, port);
99 scm_gen_putc ('>', port);
100 return 1 ;
101}
102
e137c6b3
MD
103static scm_smobfuns jbsmob = {
104 scm_mark0,
105#ifdef DEBUG_EXTENSIONS
106 freejb,
107#else
108 scm_free0,
109#endif
110 printjb,
111 0
112};
0f2d19dd 113
11702758 114static SCM make_jmpbuf SCM_P ((void));
0f2d19dd 115static SCM
11702758 116make_jmpbuf ()
0f2d19dd
JB
117{
118 SCM answer;
119 SCM_NEWCELL (answer);
7f759d79 120 SCM_REDEFER_INTS;
0f2d19dd 121 {
e137c6b3
MD
122#ifdef DEBUG_EXTENSIONS
123 char *mem = scm_must_malloc (sizeof (scm_cell), "jb");
124 SCM_SETCDR (answer, (SCM) mem);
125#endif
898a256f 126 SCM_SETCAR (answer, scm_tc16_jmpbuffer);
11702758
MD
127 SETJBJMPBUF(answer, (jmp_buf *)0);
128 DEACTIVATEJB(answer);
0f2d19dd 129 }
7f759d79 130 SCM_REALLOW_INTS;
0f2d19dd
JB
131 return answer;
132}
133
0f2d19dd
JB
134struct jmp_buf_and_retval /* use only on the stack, in scm_catch */
135{
136 jmp_buf buf; /* must be first */
137 SCM throw_tag;
138 SCM retval;
139};
140
650fa1ab
JB
141
142/* scm_internal_catch is the guts of catch. It handles all the
143 mechanics of setting up a catch target, invoking the catch body,
144 and perhaps invoking the handler if the body does a throw.
145
146 The function is designed to be usable from C code, but is general
147 enough to implement all the semantics Guile Scheme expects from
148 throw.
149
150 TAG is the catch tag. Typically, this is a symbol, but this
151 function doesn't actually care about that.
152
153 BODY is a pointer to a C function which runs the body of the catch;
154 this is the code you can throw from. We call it like this:
816a6f06 155 BODY (BODY_DATA, JMPBUF)
650fa1ab 156 where:
816a6f06
JB
157 BODY_DATA is just the BODY_DATA argument we received; we pass it
158 through to BODY as its first argument. The caller can make
159 BODY_DATA point to anything useful that BODY might need.
650fa1ab
JB
160 JMPBUF is the Scheme jmpbuf object corresponding to this catch,
161 which we have just created and initialized.
162
163 HANDLER is a pointer to a C function to deal with a throw to TAG,
164 should one occur. We call it like this:
816a6f06 165 HANDLER (HANDLER_DATA, TAG, THROW_ARGS)
650fa1ab 166 where
816a6f06
JB
167 HANDLER_DATA is the HANDLER_DATA argument we recevied; it's the
168 same idea as BODY_DATA above.
650fa1ab
JB
169 TAG is the tag that the user threw to; usually this is TAG, but
170 it could be something else if TAG was #t (i.e., a catch-all),
171 or the user threw to a jmpbuf.
172 THROW_ARGS is the list of arguments the user passed to the THROW
173 function.
174
816a6f06
JB
175 BODY_DATA is just a pointer we pass through to BODY.
176 HANDLER_DATA is just a pointer we pass through to HANDLER.
177 We don't actually use either of those pointers otherwise ourselves.
178 The idea is that, if our caller wants to communicate something to
179 BODY or HANDLER, it can pass a pointer to it as MUMBLE_DATA, which
180 BODY and HANDLER can then use. Think of it as a way to make BODY
181 and HANDLER closures, not just functions; MUMBLE_DATA points to the
182 enclosed variables. */
650fa1ab 183
0f2d19dd 184SCM
816a6f06 185scm_internal_catch (tag, body, body_data, handler, handler_data)
0f2d19dd 186 SCM tag;
650fa1ab 187 scm_catch_body_t body;
816a6f06 188 void *body_data;
650fa1ab 189 scm_catch_handler_t handler;
816a6f06 190 void *handler_data;
0f2d19dd
JB
191{
192 struct jmp_buf_and_retval jbr;
193 SCM jmpbuf;
194 SCM answer;
195
11702758 196 jmpbuf = make_jmpbuf ();
0f2d19dd
JB
197 answer = SCM_EOL;
198 scm_dynwinds = scm_acons (tag, jmpbuf, scm_dynwinds);
199 SETJBJMPBUF(jmpbuf, &jbr.buf);
200#ifdef DEBUG_EXTENSIONS
e68b42c1 201 SCM_SETJBDFRAME(jmpbuf, scm_last_debug_frame);
0f2d19dd
JB
202#endif
203 if (setjmp (jbr.buf))
204 {
205 SCM throw_tag;
206 SCM throw_args;
207
7f759d79
MD
208#ifdef STACK_CHECKING
209 scm_stack_checking_enabled_p = SCM_STACK_CHECKING_P;
210#endif
211 SCM_REDEFER_INTS;
0f2d19dd
JB
212 DEACTIVATEJB (jmpbuf);
213 scm_dynwinds = SCM_CDR (scm_dynwinds);
7f759d79 214 SCM_REALLOW_INTS;
0f2d19dd
JB
215 throw_args = jbr.retval;
216 throw_tag = jbr.throw_tag;
217 jbr.throw_tag = SCM_EOL;
218 jbr.retval = SCM_EOL;
816a6f06 219 answer = handler (handler_data, throw_tag, throw_args);
0f2d19dd
JB
220 }
221 else
222 {
223 ACTIVATEJB (jmpbuf);
816a6f06 224 answer = body (body_data, jmpbuf);
7f759d79 225 SCM_REDEFER_INTS;
0f2d19dd
JB
226 DEACTIVATEJB (jmpbuf);
227 scm_dynwinds = SCM_CDR (scm_dynwinds);
7f759d79 228 SCM_REALLOW_INTS;
0f2d19dd
JB
229 }
230 return answer;
231}
232
650fa1ab 233
816a6f06
JB
234/* This is a body function you can pass to scm_internal_catch if you
235 want the body to be like Scheme's `catch' --- a thunk, or a
236 function of one argument if the tag is #f.
650fa1ab 237
816a6f06
JB
238 DATA contains the Scheme procedure to invoke. If the tag being
239 caught is #f, then we pass JMPBUF to the body procedure; otherwise,
240 it gets no arguments. */
650fa1ab 241
816a6f06
JB
242SCM
243scm_body_thunk (body_data, jmpbuf)
244 void *body_data;
650fa1ab
JB
245 SCM jmpbuf;
246{
816a6f06 247 struct scm_body_thunk_data *c = (struct scm_body_thunk_data *) body_data;
650fa1ab
JB
248
249 if (c->tag == SCM_BOOL_F)
250 return scm_apply (c->body_proc, scm_cons (jmpbuf, SCM_EOL), SCM_EOL);
251 else
252 return scm_apply (c->body_proc, SCM_EOL, SCM_EOL);
253}
254
255
816a6f06
JB
256/* If the user does a throw to this catch, this function runs a
257 handler procedure written in Scheme. HANDLER_DATA is a pointer to
258 an SCM variable holding the Scheme procedure object to invoke. It
259 ought to be a pointer to an automatic, or the procedure object
260 should be otherwise protected from GC. */
261SCM
262scm_handle_by_proc (handler_data, tag, throw_args)
263 void *handler_data;
650fa1ab
JB
264 SCM tag;
265 SCM throw_args;
266{
816a6f06 267 SCM *handler_proc_p = (SCM *) handler_data;
650fa1ab 268
816a6f06 269 return scm_apply (*handler_proc_p, scm_cons (tag, throw_args), SCM_EOL);
650fa1ab
JB
270}
271
272
e68b42c1
MD
273SCM_PROC(s_catch, "catch", 3, 0, 0, scm_catch);
274SCM
275scm_catch (tag, thunk, handler)
276 SCM tag;
277 SCM thunk;
278 SCM handler;
279{
816a6f06 280 struct scm_body_thunk_data c;
650fa1ab 281
e68b42c1
MD
282 SCM_ASSERT ((tag == SCM_BOOL_F)
283 || (SCM_NIMP(tag) && SCM_SYMBOLP(tag))
284 || (tag == SCM_BOOL_T),
285 tag, SCM_ARG1, s_catch);
650fa1ab
JB
286
287 c.tag = tag;
288 c.body_proc = thunk;
650fa1ab
JB
289
290 /* scm_internal_catch takes care of all the mechanics of setting up
816a6f06
JB
291 a catch tag; we tell it to call scm_body_thunk to run the body,
292 and scm_handle_by_proc to deal with any throws to this catch.
293 The former receives a pointer to c, telling it how to behave.
294 The latter receives a pointer to HANDLER, so it knows who to call. */
295 return scm_internal_catch (tag,
296 scm_body_thunk, &c,
297 scm_handle_by_proc, &handler);
e68b42c1
MD
298}
299
300SCM_PROC(s_lazy_catch, "lazy-catch", 3, 0, 0, scm_lazy_catch);
301SCM
302scm_lazy_catch (tag, thunk, handler)
303 SCM tag;
304 SCM thunk;
305 SCM handler;
306{
97a307b9 307 SCM answer;
11702758 308 SCM_ASSERT ((SCM_NIMP(tag) && SCM_SYMBOLP(tag))
e68b42c1
MD
309 || (tag == SCM_BOOL_T),
310 tag, SCM_ARG1, s_lazy_catch);
97a307b9 311 SCM_REDEFER_INTS;
11702758 312 scm_dynwinds = scm_acons (tag, handler, scm_dynwinds);
97a307b9
MD
313 SCM_REALLOW_INTS;
314 answer = scm_apply (thunk, SCM_EOL, SCM_EOL);
315 SCM_REDEFER_INTS;
316 scm_dynwinds = SCM_CDR (scm_dynwinds);
317 SCM_REALLOW_INTS;
318 return answer;
e68b42c1 319}
0f2d19dd 320
816a6f06
JB
321/* The user has thrown to an uncaught key --- print a message and die.
322 At boot time, we establish a catch-all that uses this as its handler.
32f7b3a1
JB
323 1) If the user wants something different, they can use (catch #t
324 ...) to do what they like.
325 2) Outside the context of a read-eval-print loop, there isn't
326 anything else good to do; libguile should not assume the existence
327 of a read-eval-print loop.
328 3) Given that we shouldn't do anything complex, it's much more
816a6f06
JB
329 robust to do it in C code.
330
331 HANDLER_DATA, if non-zero, is assumed to be a char * pointing to a
332 message header to print; if zero, we use "guile" instead. That
333 text is followed by a colon, then the message described by ARGS. */
334
335SCM
336scm_handle_by_message (handler_data, tag, args)
337 void *handler_data;
338 SCM tag;
32f7b3a1
JB
339 SCM args;
340{
816a6f06 341 char *prog_name = (char *) handler_data;
e82afdb8
JB
342 SCM p = scm_def_errp;
343
816a6f06
JB
344 if (! prog_name)
345 prog_name = "guile";
346
347 scm_gen_puts (scm_regular_string, prog_name, p);
348 scm_gen_puts (scm_regular_string, ": ", p);
349
e82afdb8
JB
350 if (scm_ilength (args) >= 3)
351 {
352 SCM message = SCM_CADR (args);
353 SCM parts = SCM_CADDR (args);
354
e82afdb8
JB
355 scm_display_error_message (message, parts, p);
356 }
357 else
358 {
816a6f06
JB
359 scm_gen_puts (scm_regular_string, "uncaught throw to ", p);
360 scm_prin1 (tag, p, 0);
e82afdb8
JB
361 scm_gen_puts (scm_regular_string, ": ", p);
362 scm_prin1 (args, p, 1);
363 scm_gen_putc ('\n', p);
364 }
365
32f7b3a1
JB
366 exit (2);
367}
368
369
ad310508
MD
370SCM_PROC(s_throw, "throw", 1, 0, 1, scm_throw);
371SCM
372scm_throw (key, args)
373 SCM key;
374 SCM args;
375{
376 /* May return if handled by lazy catch. */
377 return scm_ithrow (key, args, 1);
378}
379
380
0f2d19dd
JB
381SCM
382scm_ithrow (key, args, noreturn)
383 SCM key;
384 SCM args;
385 int noreturn;
0f2d19dd
JB
386{
387 SCM jmpbuf;
388 SCM wind_goal;
389
390 if (SCM_NIMP (key) && SCM_JMPBUFP (key))
391 {
392 jmpbuf = key;
393 if (noreturn)
394 {
395 SCM_ASSERT (JBACTIVE (jmpbuf), jmpbuf,
396 "throw to dynamically inactive catch",
397 s_throw);
398 }
399 else if (!JBACTIVE (jmpbuf))
400 return SCM_UNSPECIFIED;
401 }
402 else
403 {
4dc2435a 404 SCM dynpair = SCM_UNDEFINED;
32f7b3a1 405 SCM winds;
0f2d19dd
JB
406
407 if (noreturn)
408 {
32f7b3a1
JB
409 SCM_ASSERT (SCM_NIMP (key) && SCM_SYMBOLP (key), key, SCM_ARG1,
410 s_throw);
0f2d19dd
JB
411 }
412 else if (!(SCM_NIMP (key) && SCM_SYMBOLP (key)))
413 return SCM_UNSPECIFIED;
414
32f7b3a1
JB
415 /* Search the wind list for an appropriate catch.
416 "Waiter, please bring us the wind list." */
b20b2777 417 for (winds = scm_dynwinds; SCM_NIMP (winds); winds = SCM_CDR (winds))
0f2d19dd 418 {
b20b2777
JB
419 if (! SCM_CONSP (winds))
420 abort ();
421
32f7b3a1 422 dynpair = SCM_CAR (winds);
b20b2777 423 if (SCM_NIMP (dynpair) && SCM_CONSP (dynpair))
32f7b3a1
JB
424 {
425 SCM this_key = SCM_CAR (dynpair);
426
427 if (this_key == SCM_BOOL_T || this_key == key)
428 break;
429 }
0f2d19dd 430 }
32f7b3a1 431
816a6f06
JB
432 /* If we didn't find anything, abort. scm_boot_guile should
433 have established a catch-all, but obviously things are
434 thoroughly screwed up. */
b20b2777 435 if (winds == SCM_EOL)
816a6f06 436 abort ();
b20b2777
JB
437
438 if (SCM_IMP (winds) || SCM_NCONSP (winds))
439 abort ();
0f2d19dd
JB
440
441 if (dynpair != SCM_BOOL_F)
442 jmpbuf = SCM_CDR (dynpair);
443 else
444 {
445 if (!noreturn)
446 return SCM_UNSPECIFIED;
447 else
448 {
449 scm_exitval = scm_cons (key, args);
450 scm_dowinds (SCM_EOL, scm_ilength (scm_dynwinds));
faa6b3df 451#ifdef DEBUG_EXTENSIONS
e68b42c1 452 scm_last_debug_frame = SCM_DFRAME (scm_rootcont);
faa6b3df 453#endif
0f2d19dd
JB
454 longjmp (SCM_JMPBUF (scm_rootcont), 1);
455 }
456 }
457 }
0f2d19dd
JB
458 for (wind_goal = scm_dynwinds;
459 SCM_CDAR (wind_goal) != jmpbuf;
460 wind_goal = SCM_CDR (wind_goal))
461 ;
11702758
MD
462 if (!SCM_JMPBUFP (jmpbuf))
463 {
97a307b9
MD
464 SCM oldwinds = scm_dynwinds;
465 SCM handle, answer;
466 scm_dowinds (wind_goal, scm_ilength (scm_dynwinds) - scm_ilength (wind_goal));
11702758 467 SCM_REDEFER_INTS;
97a307b9 468 handle = scm_dynwinds;
11702758
MD
469 scm_dynwinds = SCM_CDR (scm_dynwinds);
470 SCM_REALLOW_INTS;
97a307b9
MD
471 answer = scm_apply (jmpbuf, scm_cons (key, args), SCM_EOL);
472 SCM_REDEFER_INTS;
473 SCM_SETCDR (handle, scm_dynwinds);
474 scm_dynwinds = handle;
475 SCM_REALLOW_INTS;
476 scm_dowinds (oldwinds, scm_ilength (scm_dynwinds) - scm_ilength (oldwinds));
477 return answer;
11702758
MD
478 }
479 else
480 {
481 struct jmp_buf_and_retval * jbr;
97a307b9 482 scm_dowinds (wind_goal, scm_ilength (scm_dynwinds) - scm_ilength (wind_goal));
11702758
MD
483 jbr = (struct jmp_buf_and_retval *)JBJMPBUF (jmpbuf);
484 jbr->throw_tag = key;
485 jbr->retval = args;
486 }
faa6b3df 487#ifdef DEBUG_EXTENSIONS
e68b42c1 488 scm_last_debug_frame = SCM_JBDFRAME (jmpbuf);
faa6b3df 489#endif
0f2d19dd
JB
490 longjmp (*JBJMPBUF (jmpbuf), 1);
491}
492
493
0f2d19dd
JB
494void
495scm_init_throw ()
0f2d19dd
JB
496{
497 scm_tc16_jmpbuffer = scm_newsmob (&jbsmob);
0f2d19dd
JB
498#include "throw.x"
499}