diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2017-07-31 17:33:59 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2017-07-31 17:40:55 -0700 |
commit | 0b38bc996c4c7e2693931bbd5103c7772b56b4bd (patch) | |
tree | 8e74fd6b7efc3a0fb87037b2bb58b9d8c6129339 /unwind.c | |
parent | 2f5e7a5b96039b7a00543b4056bab7ec85c8db4b (diff) | |
download | txr-0b38bc996c4c7e2693931bbd5103c7772b56b4bd.tar.gz txr-0b38bc996c4c7e2693931bbd5103c7772b56b4bd.tar.bz2 txr-0b38bc996c4c7e2693931bbd5103c7772b56b4bd.zip |
txr-015 2009-10-15txr-015
Diffstat (limited to 'unwind.c')
-rw-r--r-- | unwind.c | 235 |
1 files changed, 227 insertions, 8 deletions
@@ -29,8 +29,11 @@ #include <assert.h> #include <setjmp.h> #include <dirent.h> +#include <stdarg.h> #include "lib.h" #include "gc.h" +#include "stream.h" +#include "txr.h" #include "unwind.h" static uw_frame_t *uw_stack; @@ -39,8 +42,39 @@ static uw_frame_t toplevel_env; static void uw_unwind_to_exit_point() { - while (uw_stack && uw_stack != uw_exit_point) - uw_stack = uw_stack->uw.up; + assert (uw_exit_point); + + for (; uw_stack && uw_stack != uw_exit_point; uw_stack = uw_stack->uw.up) { + switch (uw_stack->uw.type) { + case UW_CATCH: + /* If a catch block is not visible, do + not run its unwind stuff. This + would cause infinite loops if + unwind blocks trigger a nonlocal exit. */ + if (!uw_stack->ca.visible) + continue; + /* Catches catch everything, so that they + can do "finally" or "unwind protect" logic. + If a catch is invoked with a nil exception + and symbol, it must excecute the + mandatory clean-up code and then + continue the unwinding by calling uw_continue, + passing it the ca.cont value. */ + uw_stack->ca.sym = nil; + uw_stack->ca.exception = nil; + uw_stack->ca.cont = uw_exit_point; + /* This catch frame is no longer + visible. If the unwind section + throws something, it cannot + be caught in the same frame. */ + uw_stack->ca.visible = 0; + /* 1 means unwind only. */ + longjmp(uw_stack->ca.jb, 1); + abort(); + default: + break; + } + } if (!uw_stack) abort(); @@ -50,19 +84,21 @@ static void uw_unwind_to_exit_point() switch (uw_stack->uw.type) { case UW_BLOCK: longjmp(uw_stack->bl.jb, 1); - break; + abort(); case UW_ENV: /* env frame cannot be exit point */ abort(); + case UW_CATCH: + /* Catch frame is no longer visible. + If a catch or unwind throw something, + it cannot go back to the same catch. */ + uw_stack->ca.visible = 0; + /* 2 means actual catch, not just unwind */ + longjmp(uw_stack->ca.jb, 2); default: abort(); } } -void uw_init(void) -{ - protect(&toplevel_env.ev.func_bindings, 0); -} - void uw_push_block(uw_frame_t *fr, obj_t *tag) { fr->bl.type = UW_BLOCK; @@ -135,3 +171,186 @@ obj_t *uw_block_return(obj_t *tag, obj_t *result) uw_unwind_to_exit_point(); abort(); } + +void uw_push_catch(uw_frame_t *fr, obj_t *matches) +{ + fr->ca.type = UW_CATCH; + fr->ca.matches = matches; + fr->ca.exception = nil; + fr->ca.cont = 0; + fr->ca.visible = 1; + fr->ca.up = uw_stack; + uw_stack = fr; +} + +static obj_t *exception_subtypes; + +obj_t *uw_exception_subtype_p(obj_t *sub, obj_t *sup) +{ + if (sub == nil || sup == t || sub == sup) { + return t; + } else { + obj_t *entry = assoc(exception_subtypes, sub); + return memq(sup, cdr(entry)) ? t : nil; + } +} + +obj_t *uw_throw(obj_t *sym, obj_t *exception) +{ + uw_frame_t *ex; + + for (ex = uw_stack; ex != 0; ex = ex->uw.up) { + if (ex->uw.type == UW_CATCH && ex->ca.visible) { + /* The some_satisfy would require us to + cons up a function; we want to + avoid consing in exception handling, if we can. */ + obj_t *matches = ex->ca.matches; + obj_t *match; + for (match = matches; match; match = cdr(match)) + if (uw_exception_subtype_p(sym, car(match))) + break; + if (match) + break; + } + } + + if (ex == 0) { + if (opt_loglevel >= 1) { + format(std_error, "~a: unhandled exception of type ~a:\n", + prog_string, sym, nao); + format(std_error, "~a\n", exception, nao); + } + if (uw_exception_subtype_p(sym, query_error) || + uw_exception_subtype_p(sym, file_error)) { + if (!output_produced) + put_cstring(std_output, "false\n"); + exit(EXIT_FAILURE); + } + abort(); + } + + ex->ca.sym = sym; + ex->ca.exception = exception; + uw_exit_point = ex; + uw_unwind_to_exit_point(); + abort(); +} + +obj_t *uw_throwf(obj_t *sym, const char *fmt, ...) +{ + va_list vl; + obj_t *stream = make_string_output_stream(); + + va_start (vl, fmt); + (void) vformat(stream, fmt, vl); + va_end (vl); + + uw_throw(sym, get_string_from_stream(stream)); + abort(); +} + +obj_t *uw_errorf(const char *fmt, ...) +{ + va_list vl; + obj_t *stream = make_string_output_stream(); + + va_start (vl, fmt); + (void) vformat(stream, fmt, vl); + va_end (vl); + + uw_throw(error, get_string_from_stream(stream)); + abort(); +} + +obj_t *uw_throwcf(obj_t *sym, const char *fmt, ...) +{ + va_list vl; + obj_t *stream = make_string_output_stream(); + + va_start (vl, fmt); + (void) vcformat(stream, fmt, vl); + va_end (vl); + + uw_throw(sym, get_string_from_stream(stream)); + abort(); +} + +obj_t *uw_errorcf(const char *fmt, ...) +{ + va_list vl; + obj_t *stream = make_string_output_stream(); + + va_start (vl, fmt); + (void) vcformat(stream, fmt, vl); + va_end (vl); + + uw_throw(error, get_string_from_stream(stream)); + abort(); +} + +obj_t *type_mismatch(const char *fmt, ...) +{ + va_list vl; + obj_t *stream = make_string_output_stream(); + + va_start (vl, fmt); + (void) vformat(stream, fmt, vl); + va_end (vl); + + uw_throw(type_error, get_string_from_stream(stream)); + abort(); +} + +void uw_register_subtype(obj_t *sub, obj_t *sup) +{ + obj_t *t_entry = assoc(exception_subtypes, t); + obj_t *sub_entry = assoc(exception_subtypes, sub); + obj_t *sup_entry = assoc(exception_subtypes, sup); + + assert (t_entry != 0); + + if (sub == nil) + return; + + if (sub == t) { + if (sup == t) + return; + abort(); + } + + /* If sup symbol not registered, then we make it + an immediate subtype of t. */ + if (!sup_entry) { + sup_entry = cons(sup, t_entry); + exception_subtypes = cons(sup_entry, exception_subtypes); + } + + /* If sub already registered, we delete that + registration. */ + if (sub_entry) { + exception_subtypes = alist_remove1(exception_subtypes, sub); + } + + /* Register sub as an immediate subtype of sup. */ + sub_entry = cons(sub, sup_entry); + exception_subtypes = cons(sub_entry, exception_subtypes); +} + +void uw_continue(uw_frame_t *current, uw_frame_t *cont) +{ + uw_pop_frame(current); + uw_exit_point = cont; + uw_unwind_to_exit_point(); +} + +void uw_init(void) +{ + protect(&toplevel_env.ev.func_bindings, &exception_subtypes, 0); + exception_subtypes = cons(cons(t, cons(t, nil)), exception_subtypes); + uw_register_subtype(type_error, error); + uw_register_subtype(internal_err, error); + uw_register_subtype(numeric_err, error); + uw_register_subtype(range_err, error); + uw_register_subtype(query_error, error); + uw_register_subtype(file_error, error); +} |