diff options
-rw-r--r-- | txr.1 | 159 | ||||
-rw-r--r-- | unwind.c | 82 | ||||
-rw-r--r-- | unwind.h | 2 |
3 files changed, 242 insertions, 1 deletions
@@ -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 @@ -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)); } @@ -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); |