Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #ifndef EXCEPTION_H
- #define EXCEPTION_H
- /*
- * Simple try/except/finally implementation for C
- *
- * Inspired by http://www.adomas.org/excc/
- * Additional preprocessor abuse from http://www.chiark.greenend.org.uk/~sgtatham/mp/
- *
- * These macros aren't *that* crazy, but if you're unfamiliar with this
- * type of macro abuse, they can be scary.
- *
- * The for loops are abused for creating and shadowing variables as well
- * as for providing a construction that can consume the user's statement.
- * The also (and importantly) provide a way to execute our code before and
- * after execution of the user's statement.
- *
- * After decoding the macro abuse, this boils down to using setjmp/longjmp
- * in a rather conventional way to provide a stack of contexts to which
- * we can do non-local gotos (e.g. raise the exception) and then a loop
- * variable which determines if we are in the try/except phase or the finally
- * phase of the try/except/finally block.
- *
- * To facilitate execution of finally blocks in the event of return, I am
- * using a stack of computed gotos which record the address of the
- * finally block an allow a return macro to execute all nested finally blocks
- * during an early exit. Note: recording the address of the finally
- * block is particularly ingenious and nastly: I use GCC's statement
- * expression to label the expression-3 portion of the try's controlling
- * for-loop. This will decrement the control varible to 1, forcing us
- * into the finally phase of the block. In the case where there is no
- * finally clause, we simply fall off the bottom of the try/except clause.
- * Using this trick allows me to avoid needing a label specifier as an
- * argument to the try and finally macros.
- *
- * Similarly, the stack of computed gotos is used to get rid of
- * reraise in favor of a raise function that can figure out if it is
- * inside of a except block. This is accomplished by careful shadowing
- * of the global const int __exbody which has a value of zero.
- *
- * Every except clause shadows __exbody with a value of one. To allow
- * nesting try blocks inside of except blocks, every try block also
- * shadows __exbody with a value of zero. The raise() macro examines
- * the value of __exbody and either uses goto or longjmp depending on
- * the value. GCC can figure out the const value of __exbody and simply
- * eliminate the unused code path.
- */
- #include <npc/psetjmp.h>
- //#define THREAD __thread
- #define THREAD
- typedef struct {
- jmp_buf jb;
- // BaseException *ex;
- } exc_stack;
- extern const int __body, __exbody;
- extern int __postfinally;
- extern BaseException *__ex;
- extern THREAD int __exc_depth;
- extern THREAD exc_stack *__exc_stack;
- extern exc_stack *exc_stack_new(int depth);
- extern void exc_stack_init(int depth);
- #define EXPASTE1(x, y) x ## y
- #define EXPASTE(x, y) EXPASTE1(x, y)
- #define INIT_EXCEPTIONS() \
- const int __body = 0, __exbody = 0; \
- int __postfinally = 0; \
- BaseException *__ex = 0; \
- THREAD int __exc_depth; \
- THREAD exc_stack *__exc_stack;
- #define NOEX 0
- #define ANYEX 0
- // Helpers for managing function return from within try/except/finally
- // blocks.rt
- #define __exitinit(n) \
- any_t __retval = (any_t)-1UL; \
- int __retstk = 0; \
- int __retlbl[(n)+1] = { 0, }
- #define __rettype(type) \
- __builtin_choose_expr(__builtin_types_compatible_p(type, Boolean), __retval.b, \
- __builtin_choose_expr(__builtin_types_compatible_p(type, Int), __retval.i, \
- __builtin_choose_expr(__builtin_types_compatible_p(type, Float), __retval.f, \
- __builtin_choose_expr(__builtin_types_compatible_p(type, any_t), __retval, \
- __builtin_choose_expr(__builtin_types_compatible_p(type, uint64_t), __retval.raw, \
- __retval.p)))))
- #define __exitfini(type) __exitproc: return (type)__rettype(type)
- #define __exitpush(labelid) ({__retlbl[++__retstk] = EXPASTE(&&__exitproc, labelid) - &&__exitproc; 0;})
- #define __exitpop() (__retstk--)
- #define __exit_tgt *(&&__exitproc + __retlbl[__retstk])
- #define __exitchk() ({ if (__retval.raw != -1UL) goto __exit_tgt; })
- #define RETURN(val) ({__rettype(typeof(val))=val; goto __exit_tgt;})
- #define BREAK if (__body) {__postfinally=1; continue;} else break
- #define CONTINUE if (__body) {__postfinally=2; continue;} else continue
- // Helpers for managing the jmp_buf stack
- #define __exc_push_old() ({ \
- exc_stack *es = &__exc_stack[++__exc_depth]; \
- es->ex = NULL; \
- setjmp(es->jb); \
- es->ex; \
- })
- #define __exc_push() ({ \
- exc_stack *es = &__exc_stack[++__exc_depth]; \
- (BaseException*)psetjmp(es->jb, NULL); \
- })
- #define __exc_pop() (__exc_depth--)
- // Raise an exception
- // Use _raise in functions that do not have __exit{init,fini}
- #define _raise_old(exc) ({ \
- exc_stack *es = &__exc_stack[__exc_pop()]; \
- es->ex = (BaseException*)exc; \
- longjmp(es->jb, 1); \
- })
- #define _raise(exc) ({ \
- exc_stack *es = &__exc_stack[__exc_pop()]; \
- plongjmp(es->jb, exc); \
- })
- #define raise(exc) ({ \
- BaseException* $ex = (BaseException*)(exc); \
- if ($ex) { \
- if (__exbody) { \
- __ex=$ex; goto __exit_tgt; \
- } else { \
- _raise($ex); \
- } \
- } \
- })
- // try/except/finally: these macros are really nasty
- //
- // The first if condition is always false, but computes where to go
- // if we need to short circuit to the finally clause. It contains
- // the post-finally break and continue clauses used when
- // try/except/finally is used inside of other loops. Lastly,
- // it isolates the local namespace of the following
- // statements and the except and finally clauses.
- // The first for simply declares/shadows and initializes our control variables.
- // The second for give us a local __ex to hold the current exception.
- // The next if checks for post-finally cleanup
- // The following else-if sets the longjmp target in the event of an exception.
- // The third for executes the user supplied body once and then pops the
- // longjmp target.
- #define __try(_break, _continue) \
- if (__exitpush(__LINE__)) { \
- EXPASTE(__break, __LINE__): _break; \
- EXPASTE(__continue, __LINE__): _continue; \
- } else \
- for(int __tryctl=3,__body=0,__exbody=0,__postfinally=0; __tryctl;) \
- for(BaseException *__ex=NOEX; __tryctl; ({EXPASTE(__exitproc, __LINE__): __tryctl--;})) \
- if (__tryctl==1) { \
- if (__postfinally == 1) goto EXPASTE(__break, __LINE__); \
- if (__postfinally == 2) goto EXPASTE(__continue, __LINE__); \
- } else if (__tryctl==3 && (__ex=__exc_push()) == NOEX) \
- for(__body=1; __body; __exc_pop(), __body=0)
- // tryloop is just like try, but meant to be used inside of loops where the
- // user supplied blocks of try, except or finally contain a break or continue
- // statement.
- #define try __try(0, 0)
- #define tryloop __try(break, continue)
- // The else clause chains off of the second if in the try clause
- // and checks if we have a matching exception.
- // The for loop executes the user supplied body exactly once and then clears
- // the exception. It also shadows __exbody so the raise macro will have
- // the correct behavior within an except clause.
- #define except(cond) \
- else if (__tryctl==3 && (cond == ANYEX || _isinstance1(__ex, cond))) \
- for(int __body=1, __exbody=1; __body; __ex=NOEX, __body=0)
- // The else clause chains off of the second if in the try clause
- // and checks if we're in the finally phase of the try/except/finally
- // construct.
- // The for loop pops the exit goto context, executes the user supplied body
- // exactly once, then checks if we need to goto the exit or re-raise any
- // unhandled exception.
- #define finally \
- else if (__tryctl==2) \
- for(__body=1, __exitpop(); __body; __exitchk(), raise(__ex), __body=0)
- #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement