mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-06-24 12:20:20 +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 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 See the file guile.texi for copying conditions.
|
||||
|
||||
|
@ -606,8 +606,7 @@ more conveniently.
|
|||
@menu
|
||||
* Exception Terminology:: Different ways to say the same thing.
|
||||
* Catch:: Setting up to catch exceptions.
|
||||
* Throw Handlers:: Adding extra handling to a throw.
|
||||
* Lazy Catch:: Catch without unwinding the stack.
|
||||
* Throw Handlers:: Handling exceptions before unwinding the stack.
|
||||
* Throw:: Throwing an exception.
|
||||
* Exception Implementation:: How Guile implements exceptions.
|
||||
@end menu
|
||||
|
@ -800,17 +799,53 @@ Operations}).
|
|||
@subsubsection Throw Handlers
|
||||
|
||||
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
|
||||
will eventually be caught. This could be to clean up some related state
|
||||
or to pass information about the exception to a debugger, for example.
|
||||
The @code{with-throw-handler} procedure provides a way to do this.
|
||||
thrown before the stack is unwound. This could be to clean up some
|
||||
related state, to print a backtrace, or to pass information about the
|
||||
exception to a debugger, for example. The @code{with-throw-handler}
|
||||
procedure provides a way to do this.
|
||||
|
||||
@deffn {Scheme Procedure} 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
|
||||
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
|
||||
|
||||
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)
|
||||
The above @code{scm_with_throw_handler} takes Scheme procedures as body
|
||||
(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
|
||||
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
|
||||
@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:
|
||||
Here is an example to illustrate this behavior:
|
||||
|
||||
@lisp
|
||||
(catch 'a
|
||||
|
@ -990,14 +897,7 @@ Here is an example to illustrate the second difference:
|
|||
|
||||
@noindent
|
||||
This code will call @code{inner-handler} and then continue with the
|
||||
continuation of the inner @code{catch}. If the
|
||||
@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.
|
||||
continuation of the inner @code{catch}.
|
||||
|
||||
|
||||
@node Throw
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@c -*-texinfo-*-
|
||||
@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 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
|
||||
|
||||
@noindent
|
||||
The @code{catch} here can also be @code{lazy-catch} or
|
||||
@code{with-throw-handler}; see @ref{Throw Handlers} and @ref{Lazy Catch}
|
||||
for the details of how these differ from @code{catch}. The @code{#t}
|
||||
means that the catch is applicable to all kinds of error; if you want to
|
||||
restrict your catch to just one kind of error, you can put the symbol
|
||||
for that kind of error instead of @code{#t}. The equivalent to this in
|
||||
C would be something like this:
|
||||
The @code{catch} here can also be @code{with-throw-handler}; see @ref{Throw
|
||||
Handlers} for information on the when you might want to use
|
||||
@code{with-throw-handler} instead of @code{catch}. The @code{#t} means that the
|
||||
catch is applicable to all kinds of error; if you want to restrict your catch to
|
||||
just one kind of error, you can put the symbol for that kind of error instead of
|
||||
@code{#t}. The equivalent to this in C would be something like this:
|
||||
|
||||
@lisp
|
||||
SCM my_body_proc (void *body_data)
|
||||
|
|
|
@ -1815,6 +1815,56 @@ scm_i_subr_p (SCM 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
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "libguile/alist.h"
|
||||
#include "libguile/eval.h"
|
||||
#include "libguile/eq.h"
|
||||
#include "libguile/deprecation.h"
|
||||
#include "libguile/dynwind.h"
|
||||
#include "libguile/backtrace.h"
|
||||
#include "libguile/debug.h"
|
||||
|
@ -93,12 +94,11 @@ struct jmp_buf_and_retval /* use only on the stack, in scm_catch */
|
|||
SCM retval;
|
||||
};
|
||||
|
||||
/* These are the structures we use to store pre-unwind handling (aka
|
||||
"lazy") information for a regular catch, and put on the wind list
|
||||
for a "lazy" catch. They store the pre-unwind handler function to
|
||||
call, and the data pointer to pass through to it. It's not a
|
||||
Scheme closure, but it is a function with data, so the term
|
||||
"closure" is appropriate in its broader sense.
|
||||
/* These are the structures we use to store pre-unwind handling information for
|
||||
a regular catch, and put on the wind list for a with-throw-handler. They
|
||||
store the pre-unwind handler function to call, and the data pointer to pass
|
||||
through to it. It's not a Scheme closure, but it is a function with data, so
|
||||
the term "closure" is appropriate in its broader sense.
|
||||
|
||||
(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
|
||||
|
@ -108,7 +108,6 @@ struct pre_unwind_data {
|
|||
scm_t_catch_handler handler;
|
||||
void *handler_data;
|
||||
int running;
|
||||
int lazy_catch_p;
|
||||
};
|
||||
|
||||
|
||||
|
@ -187,7 +186,6 @@ scm_c_catch (SCM tag,
|
|||
pre_unwind.handler = pre_unwind_handler;
|
||||
pre_unwind.handler_data = pre_unwind_handler_data;
|
||||
pre_unwind.running = 0;
|
||||
pre_unwind.lazy_catch_p = 0;
|
||||
SCM_SETJBPREUNWIND(jmpbuf, &pre_unwind);
|
||||
|
||||
if (SCM_I_SETJMP (jbr.buf))
|
||||
|
@ -303,9 +301,18 @@ scm_c_with_throw_handler (SCM tag,
|
|||
c.handler = handler;
|
||||
c.handler_data = handler_data;
|
||||
c.running = 0;
|
||||
c.lazy_catch_p = lazy_catch_p;
|
||||
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_i_set_dynwinds (scm_acons (tag, pre_unwind, scm_i_dynwinds ()));
|
||||
SCM_CRITICAL_SECTION_END;
|
||||
|
@ -319,15 +326,6 @@ scm_c_with_throw_handler (SCM tag,
|
|||
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
|
||||
Use this one if you want debugging information to be stored in
|
||||
|
@ -354,7 +352,7 @@ static SCM
|
|||
cwss_body (void *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
|
||||
|
@ -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 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
|
||||
|
||||
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 */
|
||||
|
||||
|
@ -815,19 +782,7 @@ scm_ithrow (SCM key, SCM args, int noreturn SCM_UNUSED)
|
|||
{
|
||||
struct pre_unwind_data *c =
|
||||
(struct pre_unwind_data *) SCM_SMOB_DATA_1 (jmpbuf);
|
||||
SCM handle, 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;
|
||||
}
|
||||
SCM answer;
|
||||
|
||||
/* Call the handler, with framing to set the pre-unwind
|
||||
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,
|
||||
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_t_catch_body body,
|
||||
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 (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_throw (SCM key, SCM args);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
;;;; 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
|
||||
;;;; modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -306,21 +306,19 @@
|
|||
exception:wrong-type-arg
|
||||
(+ (delay (* 3 7)) 13))
|
||||
|
||||
;; Tests that require the debugging evaluator...
|
||||
(with-debugging-evaluator
|
||||
|
||||
(pass-if "unmemoizing a promise"
|
||||
(display-backtrace
|
||||
(let ((stack #f))
|
||||
(false-if-exception (lazy-catch #t
|
||||
(lambda ()
|
||||
(let ((f (lambda (g) (delay (g)))))
|
||||
(force (f error))))
|
||||
(lambda _
|
||||
(set! stack (make-stack #t)))))
|
||||
stack)
|
||||
(%make-void-port "w"))
|
||||
#t))))
|
||||
(pass-if "unmemoizing a promise"
|
||||
(display-backtrace
|
||||
(let ((stack #f))
|
||||
(false-if-exception
|
||||
(with-throw-handler #t
|
||||
(lambda ()
|
||||
(let ((f (lambda (g) (delay (g)))))
|
||||
(force (f error))))
|
||||
(lambda _
|
||||
(set! stack (make-stack #t)))))
|
||||
stack)
|
||||
(%make-void-port "w"))
|
||||
#t)))
|
||||
|
||||
|
||||
;;;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
;;;; 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
|
||||
;;;; modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -75,9 +75,9 @@
|
|||
(lambda () (throw 'a))
|
||||
(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)
|
||||
(let ((fluid-parm (make-fluid))
|
||||
(inner-val #f))
|
||||
|
@ -102,32 +102,34 @@
|
|||
(lambda (key . args)
|
||||
(push 2))))
|
||||
|
||||
(throw-test "catch and lazy catch"
|
||||
(throw-test "catch and with-throw-handler"
|
||||
'(1 2 3 4)
|
||||
(catch 'a
|
||||
(lambda ()
|
||||
(push 1)
|
||||
(lazy-catch 'a
|
||||
(lambda ()
|
||||
(push 2)
|
||||
(throw 'a))
|
||||
(lambda (key . args)
|
||||
(push 3))))
|
||||
(with-throw-handler
|
||||
'a
|
||||
(lambda ()
|
||||
(push 2)
|
||||
(throw 'a))
|
||||
(lambda (key . args)
|
||||
(push 3))))
|
||||
(lambda (key . args)
|
||||
(push 4))))
|
||||
|
||||
(throw-test "catch with rethrowing lazy catch handler"
|
||||
(throw-test "catch with rethrowing throw-handler"
|
||||
'(1 2 3 4)
|
||||
(catch 'a
|
||||
(lambda ()
|
||||
(push 1)
|
||||
(lazy-catch 'a
|
||||
(lambda ()
|
||||
(push 2)
|
||||
(throw 'a))
|
||||
(lambda (key . args)
|
||||
(push 3)
|
||||
(apply throw key args))))
|
||||
(with-throw-handler
|
||||
'a
|
||||
(lambda ()
|
||||
(push 2)
|
||||
(throw 'a))
|
||||
(lambda (key . args)
|
||||
(push 3)
|
||||
(apply throw key args))))
|
||||
(lambda (key . args)
|
||||
(push 4))))
|
||||
|
||||
|
@ -183,27 +185,6 @@
|
|||
(lambda (key . args)
|
||||
(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"
|
||||
'(1 2 3 5 4 6)
|
||||
(catch 'a
|
||||
|
@ -225,27 +206,6 @@
|
|||
(lambda (key . args)
|
||||
(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"
|
||||
'(1 2 3 4 6 8)
|
||||
(catch 'a
|
||||
|
@ -267,48 +227,6 @@
|
|||
(lambda (key . args)
|
||||
(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"
|
||||
'(1 2 3 4 8 6 10 12)
|
||||
(catch #t
|
||||
|
@ -340,37 +258,6 @@
|
|||
(lambda (key . args)
|
||||
(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"
|
||||
'(1 2 7 5 4 6 9)
|
||||
(with-throw-handler 'a
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue