summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--txr.1159
-rw-r--r--unwind.c82
-rw-r--r--unwind.h2
3 files changed, 242 insertions, 1 deletions
diff --git a/txr.1 b/txr.1
index 22d72672..f845c5ea 100644
--- a/txr.1
+++ b/txr.1
@@ -26986,6 +26986,165 @@ If that is the case, then
is returned, otherwise
.codn nil .
+.coNP Structures @, frame @ catch-frame and @ handle-frame
+.synb
+ (defstruct frame nil)
+ (defstruct catch-frame frame types jump)
+ (defstruct handle-frame frame types fun)
+.syne
+.desc
+The structure types
+.codn frame ,
+.code catch-frame
+and
+.code handle-frame
+are used by the
+.code get-frames
+function to represent information about the currently established
+exception catches (see the
+.code catch
+macro) and handlers
+(see
+.code handler-bind
+and
+.codn handler ).
+
+The
+.code frame
+type serves as the common base for
+.code catch-frame
+and
+.codn handle-frame .
+
+Modifying any of the slots of these structures has no effect on the
+actual frame from which they are derived; the frame structures are only
+representation which provides information about frames. They are not
+the actual frames themselves.
+
+Both
+.code catch-frame
+and
+.code handle-frame
+have a
+.code types
+slot. This holds the list of exception type symbols which are matched
+by the catch or handler.
+
+The
+.code jump
+slot of a
+.code catch-frame
+is an opaque
+.code cptr
+("C pointer")
+object which is related to the stack address of the catch
+frame. If it is altered, the catch frame object becomes invalid
+for the purposes of
+.codn invoke-catch .
+
+The
+.code fun
+slot of a
+.code handle-frame
+is the registered handler function. Note that all the clauses of a
+.code handler
+macro are compiled to a single function, which is established via
+.codn handler-bind ,
+so an instance of the
+.code handler
+macro corresponds to a single
+.codn handle-frame .
+
+.coNP Function @ get-frames
+.synb
+.mets (get-frames)
+.syne
+.desc
+The
+.code get-frames
+function inquires the current dynamic environment in order to retrieve
+information about established exception catch and handler frames.
+The function returns a list, ordered from the inner-most nesting
+level to the outer-most nesting, of structure objects derived from the
+.code frame
+structure type. The list contains two kinds of objects: structures
+of type
+.code catch-frame
+and of type
+.codn handle-frame .
+
+These objects are not the frames themselves, but only provide information
+about frames. Modifying the slots in these structures has no effect on
+the original frames. Also, these structures have their own lifetime and
+can endure after the original frames have disappeared. This has implications
+for the use of the
+.code invoke-catch
+function.
+
+The
+.code handle-frame
+structures have a
+.code fun
+slot, which holds a function. It may be invoked directly.
+
+A
+.code catch-frame
+structure may be passed as an argument to the
+.code invoke-catch
+function.
+
+.coNP Function @ invoke-catch
+.synb
+.mets (invoke-catch < catch-frame < symbol << argument *)
+.syne
+.desc
+The
+.code invoke-catch
+function abandons the current evaluation context to perform
+a non-local control transfer directly to the catch
+described by the
+.meta catch-frame
+argument, which must be a structure of type
+.code catch-frame
+returned by a call to
+.codn get-frames .
+
+The control transfer is possible only if the catch
+frame represented by
+.meta catch-frame
+structure is still established, and if the structure
+hasn't been tampered with.
+
+If a given
+.code catch-frame
+structure is usable with
+.codn invoke-catch ,
+then a copy of that structure made with
+.code copy-struct
+is also usable, denoting the same catch frame.
+
+The
+.meta symbol
+argument should be an exception symbol. It is passed to the
+exception frame, as if it had appeared as the first argument of the
+.code throw
+function. Similarly, the
+.metn argument -s
+are passed to the catch frame as if they were the trailing arguments
+of a
+.codn throw .
+The difference between
+.code invoke-catch
+and
+.code throw
+is that
+.code invoke-catch
+targets a specific catch frame as its exit point, rather than searching for a
+matching catch or handler frame. That specific frame receives the control.
+The frame receives control even if it it is not otherwise eligible for
+catching the exception type denoted by
+.metn symbol .
+
.SS* Regular Expression Library
.coNP Functions @ search-regex and @ range-regex
.synb
diff --git a/unwind.c b/unwind.c
index ab307dd0..8fe70159 100644
--- a/unwind.c
+++ b/unwind.c
@@ -42,6 +42,8 @@
#include "signal.h"
#include "eval.h"
#include "parser.h"
+#include "struct.h"
+#include ALLOCA_H
#include "unwind.h"
static uw_frame_t *uw_stack;
@@ -49,7 +51,9 @@ static uw_frame_t *uw_env_stack;
static uw_frame_t *uw_exit_point;
static uw_frame_t toplevel_env;
-static val unhandled_hook_s;
+static val unhandled_hook_s, types_s, jump_s;
+
+static val frame_type, catch_frame_type, handle_frame_type;
/* C99 inline instantiations. */
#if __STDC_VERSION__ >= 199901L
@@ -216,6 +220,63 @@ uw_frame_t *uw_current_exit_point(void)
return uw_exit_point;
}
+val uw_get_frames(void)
+{
+ uw_frame_t *ex;
+ list_collect_decl (out, ptail);
+
+ for (ex = uw_stack; ex != 0; ex = ex->uw.up) {
+ switch (ex->uw.type) {
+ case UW_CATCH:
+ if (ex->ca.matches && ex->ca.visible) {
+ args_decl(args, ARGS_MIN);
+ val cf = make_struct(catch_frame_type, nil, args);
+ slotset(cf, types_s, ex->ca.matches);
+ slotset(cf, jump_s, cptr(coerce(mem_t *, ex)));
+ ptail = list_collect(ptail, cf);
+ }
+ break;
+ case UW_HANDLE:
+ if (ex->ha.visible) {
+ args_decl(args, ARGS_MIN);
+ val hf = make_struct(handle_frame_type, nil, args);
+ slotset(hf, types_s, ex->ha.matches);
+ slotset(hf, fun_s, ex->ha.fun);
+ ptail = list_collect(ptail, hf);
+ }
+ default:
+ break;
+ }
+ }
+
+ return out;
+}
+
+val uw_invoke_catch(val catch_frame, val sym, struct args *args)
+{
+ uw_frame_t *ex, *ex_point;
+
+ if (struct_type(catch_frame) != catch_frame_type)
+ uw_throwf(type_error_s, lit("invoke-catch: ~s isn't a catch frame"),
+ catch_frame, nao);
+
+ ex_point = coerce(uw_frame_t *, cptr_get(slot(catch_frame, jump_s)));
+
+ for (ex = uw_stack; ex != 0; ex = ex->uw.up)
+ if (ex == ex_point && ex->uw.type == UW_CATCH)
+ break;
+
+ if (!ex)
+ uw_throwf(type_error_s, lit("invoke-catch: ~s no longer exists"),
+ catch_frame, nao);
+
+ ex->ca.sym = sym;
+ ex->ca.args = args_get_list(args);
+ uw_exit_point = ex;
+ uw_unwind_to_exit_point();
+ abort();
+}
+
val uw_block_return_proto(val tag, val result, val protocol)
{
uw_frame_t *ex;
@@ -529,6 +590,25 @@ void uw_init(void)
void uw_late_init(void)
{
+ protect(&frame_type, &catch_frame_type, &handle_frame_type,
+ convert(val *, 0));
+ types_s = intern(lit("types"), user_package);
+ jump_s = intern(lit("jump"), user_package);
+ frame_type = make_struct_type(intern(lit("frame"), user_package),
+ nil, nil, nil, nil, nil, nil);
+ catch_frame_type = make_struct_type(intern(lit("catch-frame"),
+ user_package),
+ frame_type, nil,
+ list(types_s, jump_s, nao),
+ nil, nil, nil);
+ handle_frame_type = make_struct_type(intern(lit("handle-frame"),
+ user_package),
+ frame_type, nil,
+ list(types_s, fun_s, nao),
+ nil, nil, nil);
reg_var(unhandled_hook_s = intern(lit("*unhandled-hook*"),
user_package), nil);
+ reg_fun(intern(lit("get-frames"), user_package), func_n0(uw_get_frames));
+ reg_fun(intern(lit("invoke-catch"), user_package),
+ func_n2v(uw_invoke_catch));
}
diff --git a/unwind.h b/unwind.h
index 207b8ce9..3dc9c387 100644
--- a/unwind.h
+++ b/unwind.h
@@ -126,6 +126,8 @@ void uw_pop_frame(uw_frame_t *);
void uw_pop_until(uw_frame_t *);
uw_frame_t *uw_current_frame(void);
uw_frame_t *uw_current_exit_point(void);
+val uw_get_frames(void);
+val uw_invoke_catch(val catch_frame, val sym, struct args *);
void uw_init(void);
void uw_late_init(void);