2002-01-28 Stefan Jahn <stefan@lkcc.org>
[bpt/guile.git] / libguile / throw.c
CommitLineData
22a52da1 1/* Copyright (C) 1995,1996,1997,1998,2000,2001 Free Software Foundation, Inc.
0f2d19dd
JB
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
82892bed
JB
15 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
16 * Boston, MA 02111-1307 USA
0f2d19dd
JB
17 *
18 * As a special exception, the Free Software Foundation gives permission
19 * for additional uses of the text contained in its release of GUILE.
20 *
21 * The exception is that, if you link the GUILE library with other files
22 * to produce an executable, this does not by itself cause the
23 * resulting executable to be covered by the GNU General Public License.
24 * Your use of that executable is in no way restricted on account of
25 * linking the GUILE library code into it.
26 *
27 * This exception does not however invalidate any other reasons why
28 * the executable file might be covered by the GNU General Public License.
29 *
30 * This exception applies only to the code released by the
31 * Free Software Foundation under the name GUILE. If you copy
32 * code from other Free Software Foundation releases into a copy of
33 * GUILE, as the General Public License permits, the exception does
34 * not apply to the code that you add in this way. To avoid misleading
35 * anyone as to the status of such modified files, you must delete
36 * this exception notice from them.
37 *
38 * If you write modifications of your own for GUILE, it is your choice
39 * whether to permit this exception to apply to your modifications.
82892bed 40 * If you do not wish that, delete this exception notice. */
1bbd0b84 41
1bbd0b84 42
0f2d19dd
JB
43\f
44
45#include <stdio.h>
a0599745
MD
46#include "libguile/_scm.h"
47#include "libguile/smob.h"
48#include "libguile/alist.h"
49#include "libguile/eval.h"
50#include "libguile/eq.h"
51#include "libguile/dynwind.h"
52#include "libguile/backtrace.h"
20e6290e 53#ifdef DEBUG_EXTENSIONS
a0599745 54#include "libguile/debug.h"
20e6290e 55#endif
a0599745
MD
56#include "libguile/continuations.h"
57#include "libguile/stackchk.h"
58#include "libguile/stacks.h"
59#include "libguile/fluids.h"
60#include "libguile/ports.h"
c96d76b8 61#include "libguile/lang.h"
0f2d19dd 62
a0599745
MD
63#include "libguile/validate.h"
64#include "libguile/throw.h"
0f2d19dd 65
32f7b3a1 66\f
74229f75 67/* the jump buffer data structure */
92c2555f 68static scm_t_bits tc16_jmpbuffer;
0f2d19dd 69
e841c3e0 70#define SCM_JMPBUFP(OBJ) SCM_TYP16_PREDICATE (tc16_jmpbuffer, OBJ)
c209c88e 71
e841c3e0 72#define JBACTIVE(OBJ) (SCM_CELL_WORD_0 (OBJ) & (1L << 16L))
22a52da1
DH
73#define ACTIVATEJB(x) \
74 (SCM_SET_CELL_WORD_0 ((x), (SCM_CELL_WORD_0 (x) | (1L << 16L))))
75#define DEACTIVATEJB(x) \
76 (SCM_SET_CELL_WORD_0 ((x), (SCM_CELL_WORD_0 (x) & ~(1L << 16L))))
0f2d19dd 77
4260a7fc
DH
78#define JBJMPBUF(OBJ) ((jmp_buf *) SCM_CELL_WORD_1 (OBJ))
79#define SETJBJMPBUF(x,v) (SCM_SET_CELL_WORD_1 ((x), (v)))
1a548472 80#ifdef DEBUG_EXTENSIONS
92c2555f 81#define SCM_JBDFRAME(x) ((scm_t_debug_frame *) SCM_CELL_WORD_2 (x))
1a548472 82#define SCM_SETJBDFRAME(x,v) (SCM_SET_CELL_WORD_2 ((x), (v)))
0f2d19dd
JB
83#endif
84
0f2d19dd 85static int
e81d98ec 86jmpbuffer_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
0f2d19dd 87{
b7f3516f
TT
88 scm_puts ("#<jmpbuffer ", port);
89 scm_puts (JBACTIVE(exp) ? "(active) " : "(inactive) ", port);
4260a7fc 90 scm_intprint((long) JBJMPBUF (exp), 16, port);
b7f3516f 91 scm_putc ('>', port);
0f2d19dd
JB
92 return 1 ;
93}
94
0f2d19dd 95static SCM
1bbd0b84 96make_jmpbuf (void)
0f2d19dd
JB
97{
98 SCM answer;
7f759d79 99 SCM_REDEFER_INTS;
0f2d19dd 100 {
e137c6b3 101#ifdef DEBUG_EXTENSIONS
e841c3e0 102 SCM_NEWSMOB2 (answer, tc16_jmpbuffer, 0, 0);
23a62151 103#else
e841c3e0 104 SCM_NEWSMOB (answer, tc16_jmpbuffer, 0);
23a62151 105#endif
11702758
MD
106 SETJBJMPBUF(answer, (jmp_buf *)0);
107 DEACTIVATEJB(answer);
0f2d19dd 108 }
7f759d79 109 SCM_REALLOW_INTS;
0f2d19dd
JB
110 return answer;
111}
112
74229f75 113\f
18eadcbe 114/* scm_internal_catch (the guts of catch) */
74229f75 115
0f2d19dd
JB
116struct jmp_buf_and_retval /* use only on the stack, in scm_catch */
117{
118 jmp_buf buf; /* must be first */
119 SCM throw_tag;
120 SCM retval;
121};
122
650fa1ab
JB
123
124/* scm_internal_catch is the guts of catch. It handles all the
125 mechanics of setting up a catch target, invoking the catch body,
126 and perhaps invoking the handler if the body does a throw.
127
128 The function is designed to be usable from C code, but is general
129 enough to implement all the semantics Guile Scheme expects from
130 throw.
131
132 TAG is the catch tag. Typically, this is a symbol, but this
133 function doesn't actually care about that.
134
135 BODY is a pointer to a C function which runs the body of the catch;
136 this is the code you can throw from. We call it like this:
19b27fa2 137 BODY (BODY_DATA)
650fa1ab 138 where:
816a6f06
JB
139 BODY_DATA is just the BODY_DATA argument we received; we pass it
140 through to BODY as its first argument. The caller can make
141 BODY_DATA point to anything useful that BODY might need.
650fa1ab
JB
142
143 HANDLER is a pointer to a C function to deal with a throw to TAG,
144 should one occur. We call it like this:
86327304 145 HANDLER (HANDLER_DATA, THROWN_TAG, THROW_ARGS)
650fa1ab 146 where
816a6f06
JB
147 HANDLER_DATA is the HANDLER_DATA argument we recevied; it's the
148 same idea as BODY_DATA above.
86327304
JB
149 THROWN_TAG is the tag that the user threw to; usually this is
150 TAG, but it could be something else if TAG was #t (i.e., a
151 catch-all), or the user threw to a jmpbuf.
650fa1ab 152 THROW_ARGS is the list of arguments the user passed to the THROW
4dd8323f 153 function, after the tag.
650fa1ab 154
3eed3475
JB
155 BODY_DATA is just a pointer we pass through to BODY. HANDLER_DATA
156 is just a pointer we pass through to HANDLER. We don't actually
157 use either of those pointers otherwise ourselves. The idea is
158 that, if our caller wants to communicate something to BODY or
159 HANDLER, it can pass a pointer to it as MUMBLE_DATA, which BODY and
160 HANDLER can then use. Think of it as a way to make BODY and
161 HANDLER closures, not just functions; MUMBLE_DATA points to the
162 enclosed variables.
163
164 Of course, it's up to the caller to make sure that any data a
165 MUMBLE_DATA needs is protected from GC. A common way to do this is
166 to make MUMBLE_DATA a pointer to data stored in an automatic
167 structure variable; since the collector must scan the stack for
168 references anyway, this assures that any references in MUMBLE_DATA
169 will be found. */
650fa1ab 170
0f2d19dd 171SCM
92c2555f 172scm_internal_catch (SCM tag, scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data)
0f2d19dd
JB
173{
174 struct jmp_buf_and_retval jbr;
175 SCM jmpbuf;
176 SCM answer;
177
11702758 178 jmpbuf = make_jmpbuf ();
0f2d19dd
JB
179 answer = SCM_EOL;
180 scm_dynwinds = scm_acons (tag, jmpbuf, scm_dynwinds);
181 SETJBJMPBUF(jmpbuf, &jbr.buf);
182#ifdef DEBUG_EXTENSIONS
e68b42c1 183 SCM_SETJBDFRAME(jmpbuf, scm_last_debug_frame);
0f2d19dd
JB
184#endif
185 if (setjmp (jbr.buf))
186 {
187 SCM throw_tag;
188 SCM throw_args;
189
7f759d79
MD
190#ifdef STACK_CHECKING
191 scm_stack_checking_enabled_p = SCM_STACK_CHECKING_P;
192#endif
193 SCM_REDEFER_INTS;
0f2d19dd
JB
194 DEACTIVATEJB (jmpbuf);
195 scm_dynwinds = SCM_CDR (scm_dynwinds);
7f759d79 196 SCM_REALLOW_INTS;
0f2d19dd
JB
197 throw_args = jbr.retval;
198 throw_tag = jbr.throw_tag;
199 jbr.throw_tag = SCM_EOL;
200 jbr.retval = SCM_EOL;
816a6f06 201 answer = handler (handler_data, throw_tag, throw_args);
0f2d19dd
JB
202 }
203 else
204 {
205 ACTIVATEJB (jmpbuf);
492960a4 206 answer = body (body_data);
7f759d79 207 SCM_REDEFER_INTS;
0f2d19dd
JB
208 DEACTIVATEJB (jmpbuf);
209 scm_dynwinds = SCM_CDR (scm_dynwinds);
7f759d79 210 SCM_REALLOW_INTS;
0f2d19dd
JB
211 }
212 return answer;
213}
214
650fa1ab 215
18eadcbe
JB
216\f
217/* scm_internal_lazy_catch (the guts of lazy catching) */
218
219/* The smob tag for lazy_catch smobs. */
92c2555f 220static scm_t_bits tc16_lazy_catch;
18eadcbe
JB
221
222/* This is the structure we put on the wind list for a lazy catch. It
223 stores the handler function to call, and the data pointer to pass
224 through to it. It's not a Scheme closure, but it is a function
225 with data, so the term "closure" is appropriate in its broader
226 sense.
227
228 (We don't need anything like this in the "eager" catch code,
229 because the same C frame runs both the body and the handler.) */
230struct lazy_catch {
92c2555f 231 scm_t_catch_handler handler;
18eadcbe
JB
232 void *handler_data;
233};
234
235/* Strictly speaking, we could just pass a zero for our print
236 function, because we don't need to print them. They should never
237 appear in normal data structures, only in the wind list. However,
238 it might be nice for debugging someday... */
239static int
e81d98ec 240lazy_catch_print (SCM closure, SCM port, scm_print_state *pstate SCM_UNUSED)
18eadcbe 241{
4260a7fc 242 struct lazy_catch *c = (struct lazy_catch *) SCM_CELL_WORD_1 (closure);
18eadcbe
JB
243 char buf[200];
244
245 sprintf (buf, "#<lazy-catch 0x%lx 0x%lx>",
246 (long) c->handler, (long) c->handler_data);
b7f3516f 247 scm_puts (buf, port);
18eadcbe
JB
248
249 return 1;
250}
251
18eadcbe
JB
252
253/* Given a pointer to a lazy catch structure, return a smob for it,
254