mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-06-25 04:40:19 +02:00
deprecate lazy-catch
* libguile/deprecated.h: * libguile/deprecated.c (scm_internal_lazy_catch, scm_lazy_catch): Deprecate, and print out a nasty warning that people should change to with-throw-handler. * libguile/throw.h: * libguile/throw.c (scm_c_with_throw_handler): Deprecate the use of the lazy_catch_p argument, printing out a nasty warning if someone actually passes 1 as that argument. The combination of the pre-unwind and post-unwind handlers should be sufficient. * test-suite/tests/exceptions.test: Remove lazy-catch tests, as they are deprecated. Two of them fail: * throw/catch: effect of lazy-catch unwinding on throw to another key * throw/catch: repeat of previous test but with lazy-catch Hopefully people are not depending on this behavior, and the warning is sufficiently nasty for people to switch. We will see. * test-suite/tests/eval.test ("promises"): Use with-throw-handler instead of lazy-catch. * doc/ref/api-debug.texi: * doc/ref/api-control.texi: Update to remove references to lazy-catch, folding in the useful bits to with-throw-handler.
This commit is contained in:
parent
d296431516
commit
e10cf6b9c7
8 changed files with 169 additions and 377 deletions
|
@ -1,6 +1,6 @@
|
||||||
@c -*-texinfo-*-
|
@c -*-texinfo-*-
|
||||||
@c This is part of the GNU Guile Reference Manual.
|
@c This is part of the GNU Guile Reference Manual.
|
||||||
@c Copyright (C) 1996, 1997, 2000, 2001, 2002, 2003, 2004, 2009
|
@c Copyright (C) 1996, 1997, 2000, 2001, 2002, 2003, 2004, 2009, 2010
|
||||||
@c Free Software Foundation, Inc.
|
@c Free Software Foundation, Inc.
|
||||||
@c See the file guile.texi for copying conditions.
|
@c See the file guile.texi for copying conditions.
|
||||||
|
|
||||||
|
@ -606,8 +606,7 @@ more conveniently.
|
||||||
@menu
|
@menu
|
||||||
* Exception Terminology:: Different ways to say the same thing.
|
* Exception Terminology:: Different ways to say the same thing.
|
||||||
* Catch:: Setting up to catch exceptions.
|
* Catch:: Setting up to catch exceptions.
|
||||||
* Throw Handlers:: Adding extra handling to a throw.
|
* Throw Handlers:: Handling exceptions before unwinding the stack.
|
||||||
* Lazy Catch:: Catch without unwinding the stack.
|
|
||||||
* Throw:: Throwing an exception.
|
* Throw:: Throwing an exception.
|
||||||
* Exception Implementation:: How Guile implements exceptions.
|
* Exception Implementation:: How Guile implements exceptions.
|
||||||
@end menu
|
@end menu
|
||||||
|
@ -800,17 +799,53 @@ Operations}).
|
||||||
@subsubsection Throw Handlers
|
@subsubsection Throw Handlers
|
||||||
|
|
||||||
It's sometimes useful to be able to intercept an exception that is being
|
It's sometimes useful to be able to intercept an exception that is being
|
||||||
thrown, but without changing where in the dynamic context that exception
|
thrown before the stack is unwound. This could be to clean up some
|
||||||
will eventually be caught. This could be to clean up some related state
|
related state, to print a backtrace, or to pass information about the
|
||||||
or to pass information about the exception to a debugger, for example.
|
exception to a debugger, for example. The @code{with-throw-handler}
|
||||||
The @code{with-throw-handler} procedure provides a way to do this.
|
procedure provides a way to do this.
|
||||||
|
|
||||||
@deffn {Scheme Procedure} with-throw-handler key thunk handler
|
@deffn {Scheme Procedure} with-throw-handler key thunk handler
|
||||||
@deffnx {C Function} scm_with_throw_handler (key, thunk, handler)
|
@deffnx {C Function} scm_with_throw_handler (key, thunk, handler)
|
||||||
Add @var{handler} to the dynamic context as a throw handler
|
Add @var{handler} to the dynamic context as a throw handler
|
||||||
for key @var{key}, then invoke @var{thunk}.
|
for key @var{key}, then invoke @var{thunk}.
|
||||||
|
|
||||||
|
This behaves exactly like @code{catch}, except that it does not unwind
|
||||||
|
the stack before invoking @var{handler}. If the @var{handler} procedure
|
||||||
|
returns normally, Guile rethrows the same exception again to the next
|
||||||
|
innermost catch or throw handler. @var{handler} may exit nonlocally, of
|
||||||
|
course, via an explicit throw or via invoking a continuation.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
|
Typically @var{handler} is used to display a backtrace of the stack at
|
||||||
|
the point where the corresponding @code{throw} occurred, or to save off
|
||||||
|
this information for possible display later.
|
||||||
|
|
||||||
|
Not unwinding the stack means that throwing an exception that is handled
|
||||||
|
via a throw handler is equivalent to calling the throw handler handler
|
||||||
|
inline instead of each @code{throw}, and then omitting the surrounding
|
||||||
|
@code{with-throw-handler}. In other words,
|
||||||
|
|
||||||
|
@lisp
|
||||||
|
(with-throw-handler 'key
|
||||||
|
(lambda () @dots{} (throw 'key args @dots{}) @dots{})
|
||||||
|
handler)
|
||||||
|
@end lisp
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
is mostly equivalent to
|
||||||
|
|
||||||
|
@lisp
|
||||||
|
((lambda () @dots{} (handler 'key args @dots{}) @dots{}))
|
||||||
|
@end lisp
|
||||||
|
|
||||||
|
In particular, the dynamic context when @var{handler} is invoked is that
|
||||||
|
of the site where @code{throw} is called. The examples are not quite
|
||||||
|
equivalent, because the body of a @code{with-throw-handler} is not in
|
||||||
|
tail position with respect to the @code{with-throw-handler}, and if
|
||||||
|
@var{handler} exits normally, Guile arranges to rethrow the error, but
|
||||||
|
hopefully the intention is clear. (For an introduction to what is meant
|
||||||
|
by dynamic context, @xref{Dynamic Wind}.)
|
||||||
|
|
||||||
@deftypefn {C Function} SCM scm_c_with_throw_handler (SCM tag, scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data, int lazy_catch_p)
|
@deftypefn {C Function} SCM scm_c_with_throw_handler (SCM tag, scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data, int lazy_catch_p)
|
||||||
The above @code{scm_with_throw_handler} takes Scheme procedures as body
|
The above @code{scm_with_throw_handler} takes Scheme procedures as body
|
||||||
(thunk) and handler arguments. @code{scm_c_with_throw_handler} is an
|
(thunk) and handler arguments. @code{scm_c_with_throw_handler} is an
|
||||||
|
@ -838,141 +873,13 @@ everything that a @code{catch} would do until the point where
|
||||||
then it rethrows to the next innermost @code{catch} or throw handler
|
then it rethrows to the next innermost @code{catch} or throw handler
|
||||||
instead.
|
instead.
|
||||||
|
|
||||||
|
Note also that since the dynamic context is not unwound, if a
|
||||||
|
@code{with-throw-handler} handler throws to a key that does not match
|
||||||
|
the @code{with-throw-handler} expression's @var{key}, the new throw may
|
||||||
|
be handled by a @code{catch} or throw handler that is @emph{closer} to
|
||||||
|
the throw than the first @code{with-throw-handler}.
|
||||||
|
|
||||||
@node Lazy Catch
|
Here is an example to illustrate this behavior:
|
||||||
@subsubsection Catch Without Unwinding
|
|
||||||
|
|
||||||
Before version 1.8, Guile's closest equivalent to
|
|
||||||
@code{with-throw-handler} was @code{lazy-catch}. From version 1.8
|
|
||||||
onwards we recommend using @code{with-throw-handler} because its
|
|
||||||
behaviour is more useful than that of @code{lazy-catch}, but
|
|
||||||
@code{lazy-catch} is still supported as well.
|
|
||||||
|
|
||||||
A @dfn{lazy catch} is used in the same way as a normal @code{catch},
|
|
||||||
with @var{key}, @var{thunk} and @var{handler} arguments specifying the
|
|
||||||
exception type, normal case code and handler procedure, but differs in
|
|
||||||
one important respect: the handler procedure is executed without
|
|
||||||
unwinding the call stack from the context of the @code{throw} expression
|
|
||||||
that caused the handler to be invoked.
|
|
||||||
|
|
||||||
@deffn {Scheme Procedure} lazy-catch key thunk handler
|
|
||||||
@deffnx {C Function} scm_lazy_catch (key, thunk, handler)
|
|
||||||
This behaves exactly like @code{catch}, except that it does
|
|
||||||
not unwind the stack before invoking @var{handler}.
|
|
||||||
If the @var{handler} procedure returns normally, Guile
|
|
||||||
rethrows the same exception again to the next innermost catch,
|
|
||||||
lazy-catch or throw handler. If the @var{handler} exits
|
|
||||||
non-locally, that exit determines the continuation.
|
|
||||||
@end deffn
|
|
||||||
|
|
||||||
@deftypefn {C Function} SCM scm_internal_lazy_catch (SCM tag, scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data)
|
|
||||||
The above @code{scm_lazy_catch} takes Scheme procedures as body and
|
|
||||||
handler arguments. @code{scm_internal_lazy_catch} is an equivalent
|
|
||||||
taking C functions. See @code{scm_internal_catch} (@pxref{Catch}) for
|
|
||||||
a description of the parameters, the behaviour however of course
|
|
||||||
follows @code{lazy-catch}.
|
|
||||||
@end deftypefn
|
|
||||||
|
|
||||||
Typically @var{handler} is used to display a backtrace of the stack at
|
|
||||||
the point where the corresponding @code{throw} occurred, or to save off
|
|
||||||
this information for possible display later.
|
|
||||||
|
|
||||||
Not unwinding the stack means that throwing an exception that is caught
|
|
||||||
by a @code{lazy-catch} is @emph{almost} equivalent to calling the
|
|
||||||
@code{lazy-catch}'s handler inline instead of each @code{throw}, and
|
|
||||||
then omitting the surrounding @code{lazy-catch}. In other words,
|
|
||||||
|
|
||||||
@lisp
|
|
||||||
(lazy-catch 'key
|
|
||||||
(lambda () @dots{} (throw 'key args @dots{}) @dots{})
|
|
||||||
handler)
|
|
||||||
@end lisp
|
|
||||||
|
|
||||||
@noindent
|
|
||||||
is @emph{almost} equivalent to
|
|
||||||
|
|
||||||
@lisp
|
|
||||||
((lambda () @dots{} (handler 'key args @dots{}) @dots{}))
|
|
||||||
@end lisp
|
|
||||||
|
|
||||||
@noindent
|
|
||||||
But why only @emph{almost}? The difference is that with
|
|
||||||
@code{lazy-catch} (as with normal @code{catch}), the dynamic context is
|
|
||||||
unwound back to just outside the @code{lazy-catch} expression before
|
|
||||||
invoking the handler. (For an introduction to what is meant by dynamic
|
|
||||||
context, @xref{Dynamic Wind}.)
|
|
||||||
|
|
||||||
Then, when the handler @emph{itself} throws an exception, that exception
|
|
||||||
must be caught by some kind of @code{catch} (including perhaps another
|
|
||||||
@code{lazy-catch}) higher up the call stack.
|
|
||||||
|
|
||||||
The dynamic context also includes @code{with-fluids} blocks
|
|
||||||
(@pxref{Fluids and Dynamic States}),
|
|
||||||
so the effect of unwinding the dynamic context can also be seen in fluid
|
|
||||||
variable values. This is illustrated by the following code, in which
|
|
||||||
the normal case thunk uses @code{with-fluids} to temporarily change the
|
|
||||||
value of a fluid:
|
|
||||||
|
|
||||||
@lisp
|
|
||||||
(define f (make-fluid))
|
|
||||||
(fluid-set! f "top level value")
|
|
||||||
|
|
||||||
(define (handler . args)
|
|
||||||
(cons (fluid-ref f) args))
|
|
||||||
|
|
||||||
(lazy-catch 'foo
|
|
||||||
(lambda ()
|
|
||||||
(with-fluids ((f "local value"))
|
|
||||||
(throw 'foo)))
|
|
||||||
handler)
|
|
||||||
@result{}
|
|
||||||
("top level value" foo)
|
|
||||||
|
|
||||||
((lambda ()
|
|
||||||
(with-fluids ((f "local value"))
|
|
||||||
(handler 'foo))))
|
|
||||||
@result{}
|
|
||||||
("local value" foo)
|
|
||||||
@end lisp
|
|
||||||
|
|
||||||
@noindent
|
|
||||||
In the @code{lazy-catch} version, the unwinding of dynamic context
|
|
||||||
restores @code{f} to its value outside the @code{with-fluids} block
|
|
||||||
before the handler is invoked, so the handler's @code{(fluid-ref f)}
|
|
||||||
returns the external value.
|
|
||||||
|
|
||||||
@code{lazy-catch} is useful because it permits the implementation of
|
|
||||||
debuggers and other reflective programming tools that need to access the
|
|
||||||
state of the call stack at the exact point where an exception or an
|
|
||||||
error is thrown. For an example of this, see REFFIXME:stack-catch.
|
|
||||||
|
|
||||||
It should be obvious from the above that @code{lazy-catch} is very
|
|
||||||
similar to @code{with-throw-handler}. In fact Guile implements
|
|
||||||
@code{lazy-catch} in exactly the same way as @code{with-throw-handler},
|
|
||||||
except with a flag set to say ``where there are slight differences
|
|
||||||
between what @code{with-throw-handler} and @code{lazy-catch} would do,
|
|
||||||
do what @code{lazy-catch} has always done''. There are two such
|
|
||||||
differences:
|
|
||||||
|
|
||||||
@enumerate
|
|
||||||
@item
|
|
||||||
@code{with-throw-handler} handlers execute in the full dynamic context
|
|
||||||
of the originating @code{throw} call. @code{lazy-catch} handlers
|
|
||||||
execute in the dynamic context of the @code{lazy-catch} expression,
|
|
||||||
excepting only that the stack has not yet been unwound from the point of
|
|
||||||
the @code{throw} call.
|
|
||||||
|
|
||||||
@item
|
|
||||||
If a @code{with-throw-handler} handler throws to a key that does not
|
|
||||||
match the @code{with-throw-handler} expression's @var{key}, the new
|
|
||||||
throw may be handled by a @code{catch} or throw handler that is _closer_
|
|
||||||
to the throw than the first @code{with-throw-handler}. If a
|
|
||||||
@code{lazy-catch} handler throws, it will always be handled by a
|
|
||||||
@code{catch} or throw handler that is higher up the dynamic context than
|
|
||||||
the first @code{lazy-catch}.
|
|
||||||
@end enumerate
|
|
||||||
|
|
||||||
Here is an example to illustrate the second difference:
|
|
||||||
|
|
||||||
@lisp
|
@lisp
|
||||||
(catch 'a
|
(catch 'a
|
||||||
|
@ -990,14 +897,7 @@ Here is an example to illustrate the second difference:
|
||||||
|
|
||||||
@noindent
|
@noindent
|
||||||
This code will call @code{inner-handler} and then continue with the
|
This code will call @code{inner-handler} and then continue with the
|
||||||
continuation of the inner @code{catch}. If the
|
continuation of the inner @code{catch}.
|
||||||
@code{with-throw-handler} was changed to @code{lazy-catch}, however, the
|
|
||||||
code would call @code{outer-handler} and then continue with the
|
|
||||||
continuation of the outer @code{catch}.
|
|
||||||
|
|
||||||
Modulo these two differences, any statements in the previous and
|
|
||||||
following subsections about throw handlers apply to lazy catches as
|
|
||||||
well.
|
|
||||||
|
|
||||||
|
|
||||||
@node Throw
|
@node Throw
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
@c -*-texinfo-*-
|
@c -*-texinfo-*-
|
||||||
@c This is part of the GNU Guile Reference Manual.
|
@c This is part of the GNU Guile Reference Manual.
|
||||||
@c Copyright (C) 1996, 1997, 2000, 2001, 2002, 2003, 2004, 2007
|
@c Copyright (C) 1996, 1997, 2000, 2001, 2002, 2003, 2004, 2007, 2010
|
||||||
@c Free Software Foundation, Inc.
|
@c Free Software Foundation, Inc.
|
||||||
@c See the file guile.texi for copying conditions.
|
@c See the file guile.texi for copying conditions.
|
||||||
|
|
||||||
|
@ -400,13 +400,12 @@ equivalent in C. In Scheme, this means you need something like this:
|
||||||
@end lisp
|
@end lisp
|
||||||
|
|
||||||
@noindent
|
@noindent
|
||||||
The @code{catch} here can also be @code{lazy-catch} or
|
The @code{catch} here can also be @code{with-throw-handler}; see @ref{Throw
|
||||||
@code{with-throw-handler}; see @ref{Throw Handlers} and @ref{Lazy Catch}
|
Handlers} for information on the when you might want to use
|
||||||
for the details of how these differ from @code{catch}. The @code{#t}
|
@code{with-throw-handler} instead of @code{catch}. The @code{#t} means that the
|
||||||
means that the catch is applicable to all kinds of error; if you want to
|
catch is applicable to all kinds of error; if you want to restrict your catch to
|
||||||
restrict your catch to just one kind of error, you can put the symbol
|
just one kind of error, you can put the symbol for that kind of error instead of
|
||||||
for that kind of error instead of @code{#t}. The equivalent to this in
|
@code{#t}. The equivalent to this in C would be something like this:
|
||||||
C would be something like this:
|
|
||||||
|
|
||||||
@lisp
|
@lisp
|
||||||
SCM my_body_proc (void *body_data)
|
SCM my_body_proc (void *body_data)
|
||||||
|
|
|
@ -1815,6 +1815,56 @@ scm_i_subr_p (SCM x)
|
||||||
return SCM_PRIMITIVE_P (x);
|
return SCM_PRIMITIVE_P (x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SCM
|
||||||
|
scm_internal_lazy_catch (SCM tag, scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data)
|
||||||
|
{
|
||||||
|
scm_c_issue_deprecation_warning
|
||||||
|
("`scm_internal_lazy_catch' is no longer supported. Instead this call will\n"
|
||||||
|
"dispatch to `scm_c_with_throw_handler'. Your handler will be invoked from\n"
|
||||||
|
"within the dynamic context of the corresponding `throw'.\n"
|
||||||
|
"\nTHIS COULD CHANGE YOUR PROGRAM'S BEHAVIOR.\n\n"
|
||||||
|
"Please modify your program to use `scm_c_with_throw_handler' directly,\n"
|
||||||
|
"and adapt it (if necessary) to expect to be within the dynamic context\n"
|
||||||
|
"of the throw.");
|
||||||
|
return scm_c_with_throw_handler (tag, body, body_data, handler, handler_data, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_lazy_catch, "lazy-catch", 3, 0, 0,
|
||||||
|
(SCM key, SCM thunk, SCM handler),
|
||||||
|
"This behaves exactly like @code{catch}, except that it does\n"
|
||||||
|
"not unwind the stack before invoking @var{handler}.\n"
|
||||||
|
"If the @var{handler} procedure returns normally, Guile\n"
|
||||||
|
"rethrows the same exception again to the next innermost catch,\n"
|
||||||
|
"lazy-catch or throw handler. If the @var{handler} exits\n"
|
||||||
|
"non-locally, that exit determines the continuation.")
|
||||||
|
#define FUNC_NAME s_scm_lazy_catch
|
||||||
|
{
|
||||||
|
struct scm_body_thunk_data c;
|
||||||
|
|
||||||
|
SCM_ASSERT (scm_is_symbol (key) || scm_is_eq (key, SCM_BOOL_T),
|
||||||
|
key, SCM_ARG1, FUNC_NAME);
|
||||||
|
|
||||||
|
c.tag = key;
|
||||||
|
c.body_proc = thunk;
|
||||||
|
|
||||||
|
scm_c_issue_deprecation_warning
|
||||||
|
("`lazy-catch' is no longer supported. Instead this call will dispatch\n"
|
||||||
|
"to `with-throw-handler'. Your handler will be invoked from within the\n"
|
||||||
|
"dynamic context of the corresponding `throw'.\n"
|
||||||
|
"\nTHIS COULD CHANGE YOUR PROGRAM'S BEHAVIOR.\n\n"
|
||||||
|
"Please modify your program to use `with-throw-handler' directly, and\n"
|
||||||
|
"adapt it (if necessary) to expect to be within the dynamic context of\n"
|
||||||
|
"the throw.");
|
||||||
|
|
||||||
|
return scm_c_with_throw_handler (key,
|
||||||
|
scm_body_thunk, &c,
|
||||||
|
scm_handle_by_proc, &handler, 0);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
scm_i_init_deprecated ()
|
scm_i_init_deprecated ()
|
||||||
|
|
|
@ -611,6 +611,16 @@ SCM_DEPRECATED int scm_i_subr_p (SCM x);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Deprecated 2010-01-31, use with-throw-handler instead */
|
||||||
|
SCM_DEPRECATED SCM scm_lazy_catch (SCM tag, SCM thunk, SCM handler);
|
||||||
|
SCM_DEPRECATED SCM scm_internal_lazy_catch (SCM tag,
|
||||||
|
scm_t_catch_body body,
|
||||||
|
void *body_data,
|
||||||
|
scm_t_catch_handler handler,
|
||||||
|
void *handler_data);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void scm_i_init_deprecated (void);
|
void scm_i_init_deprecated (void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "libguile/alist.h"
|
#include "libguile/alist.h"
|
||||||
#include "libguile/eval.h"
|
#include "libguile/eval.h"
|
||||||
#include "libguile/eq.h"
|
#include "libguile/eq.h"
|
||||||
|
#include "libguile/deprecation.h"
|
||||||
#include "libguile/dynwind.h"
|
#include "libguile/dynwind.h"
|
||||||
#include "libguile/backtrace.h"
|
#include "libguile/backtrace.h"
|
||||||
#include "libguile/debug.h"
|
#include "libguile/debug.h"
|
||||||
|
@ -93,12 +94,11 @@ struct jmp_buf_and_retval /* use only on the stack, in scm_catch */
|
||||||
SCM retval;
|
SCM retval;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* These are the structures we use to store pre-unwind handling (aka
|
/* These are the structures we use to store pre-unwind handling information for
|
||||||
"lazy") information for a regular catch, and put on the wind list
|
a regular catch, and put on the wind list for a with-throw-handler. They
|
||||||
for a "lazy" catch. They store the pre-unwind handler function to
|
store the pre-unwind handler function to call, and the data pointer to pass
|
||||||
call, and the data pointer to pass through to it. It's not a
|
through to it. It's not a Scheme closure, but it is a function with data, so
|
||||||
Scheme closure, but it is a function with data, so the term
|
the term "closure" is appropriate in its broader sense.
|
||||||
"closure" is appropriate in its broader sense.
|
|
||||||
|
|
||||||
(We don't need anything like this to run the normal (post-unwind)
|
(We don't need anything like this to run the normal (post-unwind)
|
||||||
catch handler, because the same C frame runs both the body and the
|
catch handler, because the same C frame runs both the body and the
|
||||||
|
@ -108,7 +108,6 @@ struct pre_unwind_data {
|
||||||
scm_t_catch_handler handler;
|
scm_t_catch_handler handler;
|
||||||
void *handler_data;
|
void *handler_data;
|
||||||
int running;
|
int running;
|
||||||
int lazy_catch_p;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -187,7 +186,6 @@ scm_c_catch (SCM tag,
|
||||||
pre_unwind.handler = pre_unwind_handler;
|
pre_unwind.handler = pre_unwind_handler;
|
||||||
pre_unwind.handler_data = pre_unwind_handler_data;
|
pre_unwind.handler_data = pre_unwind_handler_data;
|
||||||
pre_unwind.running = 0;
|
pre_unwind.running = 0;
|
||||||
pre_unwind.lazy_catch_p = 0;
|
|
||||||
SCM_SETJBPREUNWIND(jmpbuf, &pre_unwind);
|
SCM_SETJBPREUNWIND(jmpbuf, &pre_unwind);
|
||||||
|
|
||||||
if (SCM_I_SETJMP (jbr.buf))
|
if (SCM_I_SETJMP (jbr.buf))
|
||||||
|
@ -303,9 +301,18 @@ scm_c_with_throw_handler (SCM tag,
|
||||||
c.handler = handler;
|
c.handler = handler;
|
||||||
c.handler_data = handler_data;
|
c.handler_data = handler_data;
|
||||||
c.running = 0;
|
c.running = 0;
|
||||||
c.lazy_catch_p = lazy_catch_p;
|
|
||||||
pre_unwind = make_pre_unwind_data (&c);
|
pre_unwind = make_pre_unwind_data (&c);
|
||||||
|
|
||||||
|
if (lazy_catch_p)
|
||||||
|
scm_c_issue_deprecation_warning
|
||||||
|
("The LAZY_CATCH_P argument to `scm_c_with_throw_handler' is no longer.\n"
|
||||||
|
"supported. Instead the handler will be invoked from within the dynamic\n"
|
||||||
|
"context of the corresponding `throw'.\n"
|
||||||
|
"\nTHIS COULD CHANGE YOUR PROGRAM'S BEHAVIOR.\n\n"
|
||||||
|
"Please modify your program to pass 0 as the LAZY_CATCH_P argument,\n"
|
||||||
|
"and adapt it (if necessary) to expect to be within the dynamic context\n"
|
||||||
|
"of the throw.");
|
||||||
|
|
||||||
SCM_CRITICAL_SECTION_START;
|
SCM_CRITICAL_SECTION_START;
|
||||||
scm_i_set_dynwinds (scm_acons (tag, pre_unwind, scm_i_dynwinds ()));
|
scm_i_set_dynwinds (scm_acons (tag, pre_unwind, scm_i_dynwinds ()));
|
||||||
SCM_CRITICAL_SECTION_END;
|
SCM_CRITICAL_SECTION_END;
|
||||||
|
@ -319,15 +326,6 @@ scm_c_with_throw_handler (SCM tag,
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Exactly like scm_internal_catch, except:
|
|
||||||
- It does not unwind the stack (this is the major difference).
|
|
||||||
- The handler is not allowed to return. */
|
|
||||||
SCM
|
|
||||||
scm_internal_lazy_catch (SCM tag, scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data)
|
|
||||||
{
|
|
||||||
return scm_c_with_throw_handler (tag, body, body_data, handler, handler_data, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* scm_internal_stack_catch
|
/* scm_internal_stack_catch
|
||||||
Use this one if you want debugging information to be stored in
|
Use this one if you want debugging information to be stored in
|
||||||
|
@ -354,7 +352,7 @@ static SCM
|
||||||
cwss_body (void *data)
|
cwss_body (void *data)
|
||||||
{
|
{
|
||||||
struct cwss_data *d = data;
|
struct cwss_data *d = data;
|
||||||
return scm_internal_lazy_catch (d->tag, d->body, d->data, ss_handler, NULL);
|
return scm_c_with_throw_handler (d->tag, d->body, d->data, ss_handler, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
SCM
|
SCM
|
||||||
|
@ -566,7 +564,7 @@ scm_handle_by_throw (void *handler_data SCM_UNUSED, SCM tag, SCM args)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* the Scheme-visible CATCH, WITH-THROW-HANDLER and LAZY-CATCH functions */
|
/* the Scheme-visible CATCH and WITH-THROW-HANDLER functions */
|
||||||
|
|
||||||
SCM_DEFINE (scm_catch_with_pre_unwind_handler, "catch", 3, 1, 0,
|
SCM_DEFINE (scm_catch_with_pre_unwind_handler, "catch", 3, 1, 0,
|
||||||
(SCM key, SCM thunk, SCM handler, SCM pre_unwind_handler),
|
(SCM key, SCM thunk, SCM handler, SCM pre_unwind_handler),
|
||||||
|
@ -664,37 +662,6 @@ SCM_DEFINE (scm_with_throw_handler, "with-throw-handler", 3, 0, 0,
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
SCM_DEFINE (scm_lazy_catch, "lazy-catch", 3, 0, 0,
|
|
||||||
(SCM key, SCM thunk, SCM handler),
|
|
||||||
"This behaves exactly like @code{catch}, except that it does\n"
|
|
||||||
"not unwind the stack before invoking @var{handler}.\n"
|
|
||||||
"If the @var{handler} procedure returns normally, Guile\n"
|
|
||||||
"rethrows the same exception again to the next innermost catch,\n"
|
|
||||||
"lazy-catch or throw handler. If the @var{handler} exits\n"
|
|
||||||
"non-locally, that exit determines the continuation.")
|
|
||||||
#define FUNC_NAME s_scm_lazy_catch
|
|
||||||
{
|
|
||||||
struct scm_body_thunk_data c;
|
|
||||||
|
|
||||||
SCM_ASSERT (scm_is_symbol (key) || scm_is_eq (key, SCM_BOOL_T),
|
|
||||||
key, SCM_ARG1, FUNC_NAME);
|
|
||||||
|
|
||||||
c.tag = key;
|
|
||||||
c.body_proc = thunk;
|
|
||||||
|
|
||||||
/* scm_internal_lazy_catch takes care of all the mechanics of
|
|
||||||
setting up a lazy catch key; we tell it to call scm_body_thunk to
|
|
||||||
run the body, and scm_handle_by_proc to deal with any throws to
|
|
||||||
this catch. The former receives a pointer to c, telling it how
|
|
||||||
to behave. The latter receives a pointer to HANDLER, so it knows
|
|
||||||
who to call. */
|
|
||||||
return scm_internal_lazy_catch (key,
|
|
||||||
scm_body_thunk, &c,
|
|
||||||
scm_handle_by_proc, &handler);
|
|
||||||
}
|
|
||||||
#undef FUNC_NAME
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* throwing */
|
/* throwing */
|
||||||
|
|
||||||
|
@ -815,19 +782,7 @@ scm_ithrow (SCM key, SCM args, int noreturn SCM_UNUSED)
|
||||||
{
|
{
|
||||||
struct pre_unwind_data *c =
|
struct pre_unwind_data *c =
|
||||||
(struct pre_unwind_data *) SCM_SMOB_DATA_1 (jmpbuf);
|
(struct pre_unwind_data *) SCM_SMOB_DATA_1 (jmpbuf);
|
||||||
SCM handle, answer;
|
SCM answer;
|
||||||
|
|
||||||
/* For old-style lazy-catch behaviour, we unwind the dynamic
|
|
||||||
context before invoking the handler. */
|
|
||||||
if (c->lazy_catch_p)
|
|
||||||
{
|
|
||||||
scm_dowinds (wind_goal, (scm_ilength (scm_i_dynwinds ())
|
|
||||||
- scm_ilength (wind_goal)));
|
|
||||||
SCM_CRITICAL_SECTION_START;
|
|
||||||
handle = scm_i_dynwinds ();
|
|
||||||
scm_i_set_dynwinds (SCM_CDR (handle));
|
|
||||||
SCM_CRITICAL_SECTION_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Call the handler, with framing to set the pre-unwind
|
/* Call the handler, with framing to set the pre-unwind
|
||||||
structure's running field while the handler is running, so we
|
structure's running field while the handler is running, so we
|
||||||
|
|
|
@ -52,12 +52,6 @@ SCM_API SCM scm_internal_catch (SCM tag,
|
||||||
scm_t_catch_handler handler,
|
scm_t_catch_handler handler,
|
||||||
void *handler_data);
|
void *handler_data);
|
||||||
|
|
||||||
SCM_API SCM scm_internal_lazy_catch (SCM tag,
|
|
||||||
scm_t_catch_body body,
|
|
||||||
void *body_data,
|
|
||||||
scm_t_catch_handler handler,
|
|
||||||
void *handler_data);
|
|
||||||
|
|
||||||
SCM_API SCM scm_internal_stack_catch (SCM tag,
|
SCM_API SCM scm_internal_stack_catch (SCM tag,
|
||||||
scm_t_catch_body body,
|
scm_t_catch_body body,
|
||||||
void *body_data,
|
void *body_data,
|
||||||
|
@ -91,7 +85,6 @@ SCM_API int scm_exit_status (SCM args);
|
||||||
SCM_API SCM scm_catch_with_pre_unwind_handler (SCM tag, SCM thunk, SCM handler, SCM lazy_handler);
|
SCM_API SCM scm_catch_with_pre_unwind_handler (SCM tag, SCM thunk, SCM handler, SCM lazy_handler);
|
||||||
SCM_API SCM scm_catch (SCM tag, SCM thunk, SCM handler);
|
SCM_API SCM scm_catch (SCM tag, SCM thunk, SCM handler);
|
||||||
SCM_API SCM scm_with_throw_handler (SCM tag, SCM thunk, SCM handler);
|
SCM_API SCM scm_with_throw_handler (SCM tag, SCM thunk, SCM handler);
|
||||||
SCM_API SCM scm_lazy_catch (SCM tag, SCM thunk, SCM handler);
|
|
||||||
SCM_API SCM scm_ithrow (SCM key, SCM args, int noreturn);
|
SCM_API SCM scm_ithrow (SCM key, SCM args, int noreturn);
|
||||||
|
|
||||||
SCM_API SCM scm_throw (SCM key, SCM args);
|
SCM_API SCM scm_throw (SCM key, SCM args);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
;;;; eval.test --- tests guile's evaluator -*- scheme -*-
|
;;;; eval.test --- tests guile's evaluator -*- scheme -*-
|
||||||
;;;; Copyright (C) 2000, 2001, 2006, 2007, 2009 Free Software Foundation, Inc.
|
;;;; Copyright (C) 2000, 2001, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
|
||||||
;;;;
|
;;;;
|
||||||
;;;; This library is free software; you can redistribute it and/or
|
;;;; This library is free software; you can redistribute it and/or
|
||||||
;;;; modify it under the terms of the GNU Lesser General Public
|
;;;; modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -306,21 +306,19 @@
|
||||||
exception:wrong-type-arg
|
exception:wrong-type-arg
|
||||||
(+ (delay (* 3 7)) 13))
|
(+ (delay (* 3 7)) 13))
|
||||||
|
|
||||||
;; Tests that require the debugging evaluator...
|
(pass-if "unmemoizing a promise"
|
||||||
(with-debugging-evaluator
|
(display-backtrace
|
||||||
|
(let ((stack #f))
|
||||||
(pass-if "unmemoizing a promise"
|
(false-if-exception
|
||||||
(display-backtrace
|
(with-throw-handler #t
|
||||||
(let ((stack #f))
|
(lambda ()
|
||||||
(false-if-exception (lazy-catch #t
|
(let ((f (lambda (g) (delay (g)))))
|
||||||
(lambda ()
|
(force (f error))))
|
||||||
(let ((f (lambda (g) (delay (g)))))
|
(lambda _
|
||||||
(force (f error))))
|
(set! stack (make-stack #t)))))
|
||||||
(lambda _
|
stack)
|
||||||
(set! stack (make-stack #t)))))
|
(%make-void-port "w"))
|
||||||
stack)
|
#t)))
|
||||||
(%make-void-port "w"))
|
|
||||||
#t))))
|
|
||||||
|
|
||||||
|
|
||||||
;;;
|
;;;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
;;;; exceptions.test --- tests for Guile's exception handling -*- scheme -*-
|
;;;; exceptions.test --- tests for Guile's exception handling -*- scheme -*-
|
||||||
;;;; Copyright (C) 2001, 2003, 2004, 2006 Free Software Foundation, Inc.
|
;;;; Copyright (C) 2001, 2003, 2004, 2006, 2010 Free Software Foundation, Inc.
|
||||||
;;;;
|
;;;;
|
||||||
;;;; This library is free software; you can redistribute it and/or
|
;;;; This library is free software; you can redistribute it and/or
|
||||||
;;;; modify it under the terms of the GNU Lesser General Public
|
;;;; modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -75,9 +75,9 @@
|
||||||
(lambda () (throw 'a))
|
(lambda () (throw 'a))
|
||||||
(lambda (x y . rest) #f))))
|
(lambda (x y . rest) #f))))
|
||||||
|
|
||||||
(with-test-prefix "with lazy handler"
|
(with-test-prefix "with pre-unwind handler"
|
||||||
|
|
||||||
(pass-if "lazy fluid state"
|
(pass-if "pre-unwind fluid state"
|
||||||
(equal? '(inner outer arg)
|
(equal? '(inner outer arg)
|
||||||
(let ((fluid-parm (make-fluid))
|
(let ((fluid-parm (make-fluid))
|
||||||
(inner-val #f))
|
(inner-val #f))
|
||||||
|
@ -102,32 +102,34 @@
|
||||||
(lambda (key . args)
|
(lambda (key . args)
|
||||||
(push 2))))
|
(push 2))))
|
||||||
|
|
||||||
(throw-test "catch and lazy catch"
|
(throw-test "catch and with-throw-handler"
|
||||||
'(1 2 3 4)
|
'(1 2 3 4)
|
||||||
(catch 'a
|
(catch 'a
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(push 1)
|
(push 1)
|
||||||
(lazy-catch 'a
|
(with-throw-handler
|
||||||
(lambda ()
|
'a
|
||||||
(push 2)
|
(lambda ()
|
||||||
(throw 'a))
|
(push 2)
|
||||||
(lambda (key . args)
|
(throw 'a))
|
||||||
(push 3))))
|
(lambda (key . args)
|
||||||
|
(push 3))))
|
||||||
(lambda (key . args)
|
(lambda (key . args)
|
||||||
(push 4))))
|
(push 4))))
|
||||||
|
|
||||||
(throw-test "catch with rethrowing lazy catch handler"
|
(throw-test "catch with rethrowing throw-handler"
|
||||||
'(1 2 3 4)
|
'(1 2 3 4)
|
||||||
(catch 'a
|
(catch 'a
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(push 1)
|
(push 1)
|
||||||
(lazy-catch 'a
|
(with-throw-handler
|
||||||
(lambda ()
|
'a
|
||||||
(push 2)
|
(lambda ()
|
||||||
(throw 'a))
|
(push 2)
|
||||||
(lambda (key . args)
|
(throw 'a))
|
||||||
(push 3)
|
(lambda (key . args)
|
||||||
(apply throw key args))))
|
(push 3)
|
||||||
|
(apply throw key args))))
|
||||||
(lambda (key . args)
|
(lambda (key . args)
|
||||||
(push 4))))
|
(push 4))))
|
||||||
|
|
||||||
|
@ -183,27 +185,6 @@
|
||||||
(lambda (key . args)
|
(lambda (key . args)
|
||||||
(push 4))))
|
(push 4))))
|
||||||
|
|
||||||
(throw-test "effect of lazy-catch unwinding on throw to another key"
|
|
||||||
'(1 2 3 5 7)
|
|
||||||
(catch 'a
|
|
||||||
(lambda ()
|
|
||||||
(push 1)
|
|
||||||
(lazy-catch 'b
|
|
||||||
(lambda ()
|
|
||||||
(push 2)
|
|
||||||
(catch 'a
|
|
||||||
(lambda ()
|
|
||||||
(push 3)
|
|
||||||
(throw 'b))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 4))))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 5)
|
|
||||||
(throw 'a)))
|
|
||||||
(push 6))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 7))))
|
|
||||||
|
|
||||||
(throw-test "effect of with-throw-handler not-unwinding on throw to another key"
|
(throw-test "effect of with-throw-handler not-unwinding on throw to another key"
|
||||||
'(1 2 3 5 4 6)
|
'(1 2 3 5 4 6)
|
||||||
(catch 'a
|
(catch 'a
|
||||||
|
@ -225,27 +206,6 @@
|
||||||
(lambda (key . args)
|
(lambda (key . args)
|
||||||
(push 7))))
|
(push 7))))
|
||||||
|
|
||||||
(throw-test "lazy-catch chaining"
|
|
||||||
'(1 2 3 4 6 8)
|
|
||||||
(catch 'a
|
|
||||||
(lambda ()
|
|
||||||
(push 1)
|
|
||||||
(lazy-catch 'a
|
|
||||||
(lambda ()
|
|
||||||
(push 2)
|
|
||||||
(lazy-catch 'a
|
|
||||||
(lambda ()
|
|
||||||
(push 3)
|
|
||||||
(throw 'a))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 4)))
|
|
||||||
(push 5))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 6)))
|
|
||||||
(push 7))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 8))))
|
|
||||||
|
|
||||||
(throw-test "with-throw-handler chaining"
|
(throw-test "with-throw-handler chaining"
|
||||||
'(1 2 3 4 6 8)
|
'(1 2 3 4 6 8)
|
||||||
(catch 'a
|
(catch 'a
|
||||||
|
@ -267,48 +227,6 @@
|
||||||
(lambda (key . args)
|
(lambda (key . args)
|
||||||
(push 8))))
|
(push 8))))
|
||||||
|
|
||||||
(throw-test "with-throw-handler inside lazy-catch"
|
|
||||||
'(1 2 3 4 6 8)
|
|
||||||
(catch 'a
|
|
||||||
(lambda ()
|
|
||||||
(push 1)
|
|
||||||
(lazy-catch 'a
|
|
||||||
(lambda ()
|
|
||||||
(push 2)
|
|
||||||
(with-throw-handler 'a
|
|
||||||
(lambda ()
|
|
||||||
(push 3)
|
|
||||||
(throw 'a))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 4)))
|
|
||||||
(push 5))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 6)))
|
|
||||||
(push 7))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 8))))
|
|
||||||
|
|
||||||
(throw-test "lazy-catch inside with-throw-handler"
|
|
||||||
'(1 2 3 4 6 8)
|
|
||||||
(catch 'a
|
|
||||||
(lambda ()
|
|
||||||
(push 1)
|
|
||||||
(with-throw-handler 'a
|
|
||||||
(lambda ()
|
|
||||||
(push 2)
|
|
||||||
(lazy-catch 'a
|
|
||||||
(lambda ()
|
|
||||||
(push 3)
|
|
||||||
(throw 'a))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 4)))
|
|
||||||
(push 5))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 6)))
|
|
||||||
(push 7))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 8))))
|
|
||||||
|
|
||||||
(throw-test "throw handlers throwing to each other recursively"
|
(throw-test "throw handlers throwing to each other recursively"
|
||||||
'(1 2 3 4 8 6 10 12)
|
'(1 2 3 4 8 6 10 12)
|
||||||
(catch #t
|
(catch #t
|
||||||
|
@ -340,37 +258,6 @@
|
||||||
(lambda (key . args)
|
(lambda (key . args)
|
||||||
(push 12))))
|
(push 12))))
|
||||||
|
|
||||||
(throw-test "repeat of previous test but with lazy-catch"
|
|
||||||
'(1 2 3 4 8 12)
|
|
||||||
(catch #t
|
|
||||||
(lambda ()
|
|
||||||
(push 1)
|
|
||||||
(lazy-catch 'a
|
|
||||||
(lambda ()
|
|
||||||
(push 2)
|
|
||||||
(lazy-catch 'b
|
|
||||||
(lambda ()
|
|
||||||
(push 3)
|
|
||||||
(lazy-catch 'c
|
|
||||||
(lambda ()
|
|
||||||
(push 4)
|
|
||||||
(throw 'b)
|
|
||||||
(push 5))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 6)
|
|
||||||
(throw 'a)))
|
|
||||||
(push 7))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 8)
|
|
||||||
(throw 'c)))
|
|
||||||
(push 9))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 10)
|
|
||||||
(throw 'b)))
|
|
||||||
(push 11))
|
|
||||||
(lambda (key . args)
|
|
||||||
(push 12))))
|
|
||||||
|
|
||||||
(throw-test "throw handler throwing to lexically inside catch"
|
(throw-test "throw handler throwing to lexically inside catch"
|
||||||
'(1 2 7 5 4 6 9)
|
'(1 2 7 5 4 6 9)
|
||||||
(with-throw-handler 'a
|
(with-throw-handler 'a
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue