diff --git a/Makefile.rules b/Makefile.rules index 07103a78df..5c808fde79 100644 --- a/Makefile.rules +++ b/Makefile.rules @@ -1090,7 +1090,7 @@ testbugfix: all check: all $(TESTGAP) $(top_srcdir)/tst/testinstall.g -LIBGAPTESTS := $(addprefix tst/testlibgap/,basic api wscreate wsload) +LIBGAPTESTS := $(addprefix tst/testlibgap/,basic api wscreate wsload trycatch) # run a test in tst/testlibgap tst/testlibgap/%: build/obj/tst/testlibgap/%.c.lo build/obj/tst/testlibgap/common.c.lo libgap.la diff --git a/src/sysjmp.c b/src/sysjmp.c index 3cfb35a9ae..90aa7a9792 100644 --- a/src/sysjmp.c +++ b/src/sysjmp.c @@ -28,10 +28,9 @@ enum { signalSyLongjmpFuncsLen = 16 }; static voidfunc signalSyLongjmpFuncs[signalSyLongjmpFuncsLen]; -Int RegisterSyLongjmpObserver(voidfunc func) +int RegisterSyLongjmpObserver(voidfunc func) { - Int i; - for (i = 0; i < signalSyLongjmpFuncsLen; ++i) { + for (int i = 0; i < signalSyLongjmpFuncsLen; ++i) { if (signalSyLongjmpFuncs[i] == func) { return 1; } @@ -43,10 +42,33 @@ Int RegisterSyLongjmpObserver(voidfunc func) return 0; } +enum { tryCatchFuncsLen = 16 }; + +static TryCatchHandler tryCatchFuncs[tryCatchFuncsLen]; + +int RegisterTryCatchHandler(TryCatchHandler func) +{ + for (int i = 0; i < tryCatchFuncsLen; ++i) { + if (tryCatchFuncs[i] == func) { + return 1; + } + if (tryCatchFuncs[i] == 0) { + tryCatchFuncs[i] = func; + return 1; + } + } + return 0; +} + +void InvokeTryCatchHandler(TryCatchMode mode) +{ + for (int i = 0; i < tryCatchFuncsLen && tryCatchFuncs[i]; ++i) + (tryCatchFuncs[i])(mode); +} + void GAP_THROW(void) { - Int i; - for (i = 0; i < signalSyLongjmpFuncsLen && signalSyLongjmpFuncs[i]; ++i) + for (int i = 0; i < signalSyLongjmpFuncsLen && signalSyLongjmpFuncs[i]; ++i) (signalSyLongjmpFuncs[i])(); longjmp(STATE(ReadJmpError), 1); } diff --git a/src/sysjmp.h b/src/sysjmp.h index 7938575089..bcd560510c 100644 --- a/src/sysjmp.h +++ b/src/sysjmp.h @@ -25,6 +25,6 @@ ** This function is idempotent -- if a function is passed multiple times ** it is still only registered once. */ -Int RegisterSyLongjmpObserver(voidfunc); +int RegisterSyLongjmpObserver(voidfunc); #endif // GAP_SYSJMP_H diff --git a/src/trycatch.h b/src/trycatch.h index aab607ac6c..5dc711986d 100644 --- a/src/trycatch.h +++ b/src/trycatch.h @@ -15,9 +15,37 @@ #include "funcs.h" // for SetRecursionDepth #include "gapstate.h" +#include "system.h" // for NORETURN #include // for memcpy +/**************************************************************************** +** +*T TryCatchMode +*T TryCatchHandler +*F RegisterTryCatchHandler() +*F InvokeTryCatchHandler() +** +** The function RegisterTryCatchObserver() allows the installation of +** global exception handlers that are being called whenever GAP_TRY or +** CALL_WITH_CATCH() code is executed. It returns 1 if installing the +** handler was successful, 0 otherwise. Installation can only fail if one +** attempts to install more handlers than the allotted maximum (currently +** 16). +** +** The mode parameter of the handler function signals whether it has been +** called at the beginning of the section, at the end of the section +** without an error being raised, or at the end of a section with an error +** being raised, respectively. The function InvokeTryCatchObserver() is +** used to invoke those handlers as needed. +*/ +typedef enum { TryEnter = 0, TryLeave = 1, TryCatch = 2 } TryCatchMode; + +typedef void (*TryCatchHandler)(TryCatchMode mode); + +int RegisterTryCatchHandler(TryCatchHandler func); +void InvokeTryCatchHandler(TryCatchMode mode); + /**************************************************************************** ** @@ -60,12 +88,14 @@ jmp_buf gap__jmp_buf; \ volatile Int gap__recursionDepth = GetRecursionDepth(); \ memcpy(gap__jmp_buf, STATE(ReadJmpError), sizeof(jmp_buf)); \ + InvokeTryCatchHandler(TryEnter); \ if (!setjmp(STATE(ReadJmpError))) \ for (gap__i = 1; gap__i; gap__i = 0, \ + InvokeTryCatchHandler(TryLeave), \ gap_restore_trycatch(gap__jmp_buf, gap__recursionDepth)) #define GAP_CATCH \ - else for (gap__j = 1, \ + else for (gap__j = 1, InvokeTryCatchHandler(TryCatch), \ gap_restore_trycatch(gap__jmp_buf, gap__recursionDepth); \ gap__j; gap__j = 0) diff --git a/tst/testlibgap/trycatch.c b/tst/testlibgap/trycatch.c new file mode 100644 index 0000000000..11b9fa0702 --- /dev/null +++ b/tst/testlibgap/trycatch.c @@ -0,0 +1,42 @@ +/* + * Small program to test libgap linkability and basic working + */ +#include "trycatch.h" +#include "common.h" + +static int level = 0; + +static void handle_trycatch(TryCatchMode mode) +{ + switch (mode) { + case TryEnter: + level++; + if (level == 1) + printf("Entering GAP_TRY section\n"); + break; + case TryLeave: + if (level == 1) + printf("Leaving GAP_TRY section\n"); + level--; + break; + case TryCatch: + if (level == 1) + printf("Caught error in GAP_TRY section\n"); + level--; + break; + } +} + +int main(int argc, char ** argv) +{ + printf("# Initializing GAP...\n"); + GAP_Initialize(argc, argv, 0, 0, 1); + RegisterTryCatchHandler(handle_trycatch); + test_eval("OnBreak := false;;"); + // Necessary to redirect error printing to stdout. + test_eval("MakeReadWriteGVar(\"ERROR_OUTPUT\");"); + test_eval("ERROR_OUTPUT := MakeImmutable(\"*stdout*\");;"); + test_eval("Display(CALL_WITH_CATCH(function() return 314; end, []));;"); + test_eval("Display(CALL_WITH_CATCH(function() return [][1]; end, []));;"); + return 0; +} diff --git a/tst/testlibgap/trycatch.expect b/tst/testlibgap/trycatch.expect new file mode 100644 index 0000000000..d8830f6a6c --- /dev/null +++ b/tst/testlibgap/trycatch.expect @@ -0,0 +1,18 @@ +# Initializing GAP... +gap> OnBreak := false;; + +gap> MakeReadWriteGVar("ERROR_OUTPUT"); + +gap> ERROR_OUTPUT := MakeImmutable("*stdout*");; + +gap> Display(CALL_WITH_CATCH(function() return 314; end, []));; +Entering GAP_TRY section +Leaving GAP_TRY section +[ true, 314 ] + +gap> Display(CALL_WITH_CATCH(function() return [][1]; end, []));; +Entering GAP_TRY section +Error, List Element: [1] must have an assigned value +Caught error in GAP_TRY section +[ false, 0 ] +