diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/eval.c | 97 | ||||
| -rw-r--r-- | src/lisp.h | 41 |
2 files changed, 121 insertions, 17 deletions
diff --git a/src/eval.c b/src/eval.c index 7f67b5a9db8..595267f7686 100644 --- a/src/eval.c +++ b/src/eval.c | |||
| @@ -1198,6 +1198,12 @@ usage: (catch TAG BODY...) */) | |||
| 1198 | 1198 | ||
| 1199 | #define clobbered_eassert(E) verify (sizeof (E) != 0) | 1199 | #define clobbered_eassert(E) verify (sizeof (E) != 0) |
| 1200 | 1200 | ||
| 1201 | static void | ||
| 1202 | pop_handler (void) | ||
| 1203 | { | ||
| 1204 | handlerlist = handlerlist->next; | ||
| 1205 | } | ||
| 1206 | |||
| 1201 | /* Set up a catch, then call C function FUNC on argument ARG. | 1207 | /* Set up a catch, then call C function FUNC on argument ARG. |
| 1202 | FUNC should return a Lisp_Object. | 1208 | FUNC should return a Lisp_Object. |
| 1203 | This is how catches are done from within C code. */ | 1209 | This is how catches are done from within C code. */ |
| @@ -1361,6 +1367,43 @@ usage: (condition-case VAR BODYFORM &rest HANDLERS) */) | |||
| 1361 | return internal_lisp_condition_case (var, bodyform, handlers); | 1367 | return internal_lisp_condition_case (var, bodyform, handlers); |
| 1362 | } | 1368 | } |
| 1363 | 1369 | ||
| 1370 | DEFUN ("handler-bind-1", Fhandler_bind_1, Shandler_bind_1, 1, MANY, 0, | ||
| 1371 | doc: /* Setup error handlers around execution of BODYFUN. | ||
| 1372 | BODYFUN be a function and it is called with no arguments. | ||
| 1373 | CONDITIONS should be a list of condition names (symbols). | ||
| 1374 | When an error is signaled during executon of BODYFUN, if that | ||
| 1375 | error matches one of CONDITIONS, then the associated HANDLER is | ||
| 1376 | called with the error as argument. | ||
| 1377 | HANDLER should either transfer the control via a non-local exit, | ||
| 1378 | or return normally. | ||
| 1379 | If it returns normally, the search for an error handler continues | ||
| 1380 | from where it left off. | ||
| 1381 | |||
| 1382 | usage: (handler-bind BODYFUN [CONDITIONS HANDLER]...) */) | ||
| 1383 | (ptrdiff_t nargs, Lisp_Object *args) | ||
| 1384 | { | ||
| 1385 | eassert (nargs >= 1); | ||
| 1386 | Lisp_Object bodyfun = args[0]; | ||
| 1387 | int count = 0; | ||
| 1388 | if (nargs % 2 == 0) | ||
| 1389 | error ("Trailing CONDITIONS withount HANDLER in `handler-bind`"); | ||
| 1390 | for (ptrdiff_t i = nargs - 2; i > 0; i -= 2) | ||
| 1391 | { | ||
| 1392 | Lisp_Object conditions = args[i], handler = args[i + 1]; | ||
| 1393 | if (NILP (conditions)) | ||
| 1394 | continue; | ||
| 1395 | else if (!CONSP (conditions)) | ||
| 1396 | conditions = Fcons (conditions, Qnil); | ||
| 1397 | struct handler *c = push_handler (conditions, HANDLER_BIND); | ||
| 1398 | c->val = handler; | ||
| 1399 | c->bytecode_dest = count++; | ||
| 1400 | } | ||
| 1401 | Lisp_Object ret = call0 (bodyfun); | ||
| 1402 | for (; count > 0; count--) | ||
| 1403 | pop_handler (); | ||
| 1404 | return ret; | ||
| 1405 | } | ||
| 1406 | |||
| 1364 | /* Like Fcondition_case, but the args are separate | 1407 | /* Like Fcondition_case, but the args are separate |
| 1365 | rather than passed in a list. Used by Fbyte_code. */ | 1408 | rather than passed in a list. Used by Fbyte_code. */ |
| 1366 | 1409 | ||
| @@ -1737,6 +1780,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1737 | = (NILP (error_symbol) ? Fcar (data) : error_symbol); | 1780 | = (NILP (error_symbol) ? Fcar (data) : error_symbol); |
| 1738 | Lisp_Object clause = Qnil; | 1781 | Lisp_Object clause = Qnil; |
| 1739 | struct handler *h; | 1782 | struct handler *h; |
| 1783 | int skip; | ||
| 1740 | 1784 | ||
| 1741 | if (gc_in_progress || waiting_for_input) | 1785 | if (gc_in_progress || waiting_for_input) |
| 1742 | emacs_abort (); | 1786 | emacs_abort (); |
| @@ -1759,6 +1803,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1759 | /* Edebug takes care of restoring these variables when it exits. */ | 1803 | /* Edebug takes care of restoring these variables when it exits. */ |
| 1760 | max_ensure_room (&max_lisp_eval_depth, lisp_eval_depth, 20); | 1804 | max_ensure_room (&max_lisp_eval_depth, lisp_eval_depth, 20); |
| 1761 | 1805 | ||
| 1806 | /* FIXME: 'handler-bind' makes `signal-hook-function' obsolete? */ | ||
| 1762 | call2 (Vsignal_hook_function, error_symbol, data); | 1807 | call2 (Vsignal_hook_function, error_symbol, data); |
| 1763 | } | 1808 | } |
| 1764 | 1809 | ||
| @@ -1778,16 +1823,42 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1778 | Vsignaling_function = backtrace_function (pdl); | 1823 | Vsignaling_function = backtrace_function (pdl); |
| 1779 | } | 1824 | } |
| 1780 | 1825 | ||
| 1781 | for (h = handlerlist; h; h = h->next) | 1826 | for (skip = 0, h = handlerlist; h; skip++, h = h->next) |
| 1782 | { | 1827 | { |
| 1783 | if (h->type == CATCHER_ALL) | 1828 | switch (h->type) |
| 1784 | { | 1829 | { |
| 1830 | case CATCHER_ALL: | ||
| 1785 | clause = Qt; | 1831 | clause = Qt; |
| 1786 | break; | 1832 | break; |
| 1787 | } | 1833 | case CATCHER: |
| 1788 | if (h->type != CONDITION_CASE) | 1834 | continue; |
| 1789 | continue; | 1835 | case CONDITION_CASE: |
| 1790 | clause = find_handler_clause (h->tag_or_ch, conditions); | 1836 | clause = find_handler_clause (h->tag_or_ch, conditions); |
| 1837 | break; | ||
| 1838 | case HANDLER_BIND: | ||
| 1839 | { | ||
| 1840 | if (!NILP (find_handler_clause (h->tag_or_ch, conditions))) | ||
| 1841 | { | ||
| 1842 | Lisp_Object error_data | ||
| 1843 | = (NILP (error_symbol) | ||
| 1844 | ? data : Fcons (error_symbol, data)); | ||
| 1845 | push_handler (make_fixnum (skip + h->bytecode_dest), | ||
| 1846 | SKIP_CONDITIONS); | ||
| 1847 | call1 (h->val, error_data); | ||
| 1848 | pop_handler (); | ||
| 1849 | } | ||
| 1850 | continue; | ||
| 1851 | } | ||
| 1852 | case SKIP_CONDITIONS: | ||
| 1853 | { | ||
| 1854 | int toskip = XFIXNUM (h->tag_or_ch); | ||
| 1855 | while (toskip-- >= 0) | ||
| 1856 | h = h->next; | ||
| 1857 | continue; | ||
| 1858 | } | ||
| 1859 | default: | ||
| 1860 | abort (); | ||
| 1861 | } | ||
| 1791 | if (!NILP (clause)) | 1862 | if (!NILP (clause)) |
| 1792 | break; | 1863 | break; |
| 1793 | } | 1864 | } |
| @@ -1804,7 +1875,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1804 | || (CONSP (clause) && !NILP (Fmemq (Qdebug, clause))) | 1875 | || (CONSP (clause) && !NILP (Fmemq (Qdebug, clause))) |
| 1805 | /* Special handler that means "print a message and run debugger | 1876 | /* Special handler that means "print a message and run debugger |
| 1806 | if requested". */ | 1877 | if requested". */ |
| 1807 | || EQ (h->tag_or_ch, Qerror))) | 1878 | || EQ (clause, Qerror))) |
| 1808 | { | 1879 | { |
| 1809 | debugger_called | 1880 | debugger_called |
| 1810 | = maybe_call_debugger (conditions, error_symbol, data); | 1881 | = maybe_call_debugger (conditions, error_symbol, data); |
| @@ -1818,8 +1889,9 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1818 | with debugging. Make sure to use `debug-early' unconditionally | 1889 | with debugging. Make sure to use `debug-early' unconditionally |
| 1819 | to not interfere with ERT or other packages that install custom | 1890 | to not interfere with ERT or other packages that install custom |
| 1820 | debuggers. */ | 1891 | debuggers. */ |
| 1892 | /* FIXME: This could be turned into a `handler-bind` at toplevel? */ | ||
| 1821 | if (!debugger_called && !NILP (error_symbol) | 1893 | if (!debugger_called && !NILP (error_symbol) |
| 1822 | && (NILP (clause) || EQ (h->tag_or_ch, Qerror)) | 1894 | && (NILP (clause) || EQ (clause, Qerror)) |
| 1823 | && noninteractive && backtrace_on_error_noninteractive | 1895 | && noninteractive && backtrace_on_error_noninteractive |
| 1824 | && NILP (Vinhibit_debugger) | 1896 | && NILP (Vinhibit_debugger) |
| 1825 | && !NILP (Ffboundp (Qdebug_early))) | 1897 | && !NILP (Ffboundp (Qdebug_early))) |
| @@ -1833,6 +1905,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) | |||
| 1833 | 1905 | ||
| 1834 | /* If an error is signaled during a Lisp hook in redisplay, write a | 1906 | /* If an error is signaled during a Lisp hook in redisplay, write a |
| 1835 | backtrace into the buffer *Redisplay-trace*. */ | 1907 | backtrace into the buffer *Redisplay-trace*. */ |
| 1908 | /* FIXME: Turn this into a `handler-bind` installed during redisplay? */ | ||
| 1836 | if (!debugger_called && !NILP (error_symbol) | 1909 | if (!debugger_called && !NILP (error_symbol) |
| 1837 | && backtrace_on_redisplay_error | 1910 | && backtrace_on_redisplay_error |
| 1838 | && (NILP (clause) || h == redisplay_deep_handler) | 1911 | && (NILP (clause) || h == redisplay_deep_handler) |
| @@ -2058,13 +2131,10 @@ find_handler_clause (Lisp_Object handlers, Lisp_Object conditions) | |||
| 2058 | register Lisp_Object h; | 2131 | register Lisp_Object h; |
| 2059 | 2132 | ||
| 2060 | /* t is used by handlers for all conditions, set up by C code. */ | 2133 | /* t is used by handlers for all conditions, set up by C code. */ |
| 2061 | if (EQ (handlers, Qt)) | ||
| 2062 | return Qt; | ||
| 2063 | |||
| 2064 | /* error is used similarly, but means print an error message | 2134 | /* error is used similarly, but means print an error message |
| 2065 | and run the debugger if that is enabled. */ | 2135 | and run the debugger if that is enabled. */ |
| 2066 | if (EQ (handlers, Qerror)) | 2136 | if (!CONSP (handlers)) |
| 2067 | return Qt; | 2137 | return handlers; |
| 2068 | 2138 | ||
| 2069 | for (h = handlers; CONSP (h); h = XCDR (h)) | 2139 | for (h = handlers; CONSP (h); h = XCDR (h)) |
| 2070 | { | 2140 | { |
| @@ -4494,6 +4564,7 @@ alist of active lexical bindings. */); | |||
| 4494 | defsubr (&Sthrow); | 4564 | defsubr (&Sthrow); |
| 4495 | defsubr (&Sunwind_protect); | 4565 | defsubr (&Sunwind_protect); |
| 4496 | defsubr (&Scondition_case); | 4566 | defsubr (&Scondition_case); |
| 4567 | defsubr (&Shandler_bind_1); | ||
| 4497 | DEFSYM (QCsuccess, ":success"); | 4568 | DEFSYM (QCsuccess, ":success"); |
| 4498 | defsubr (&Ssignal); | 4569 | defsubr (&Ssignal); |
| 4499 | defsubr (&Scommandp); | 4570 | defsubr (&Scommandp); |
diff --git a/src/lisp.h b/src/lisp.h index 10018e4dde7..2b30326abfc 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -3543,7 +3543,8 @@ record_in_backtrace (Lisp_Object function, Lisp_Object *args, ptrdiff_t nargs) | |||
| 3543 | } | 3543 | } |
| 3544 | 3544 | ||
| 3545 | /* This structure helps implement the `catch/throw' and `condition-case/signal' | 3545 | /* This structure helps implement the `catch/throw' and `condition-case/signal' |
| 3546 | control structures. A struct handler contains all the information needed to | 3546 | control structures as well as 'handler-bind'. |
| 3547 | A struct handler contains all the information needed to | ||
| 3547 | restore the state of the interpreter after a non-local jump. | 3548 | restore the state of the interpreter after a non-local jump. |
| 3548 | 3549 | ||
| 3549 | Handler structures are chained together in a doubly linked list; the `next' | 3550 | Handler structures are chained together in a doubly linked list; the `next' |
| @@ -3564,9 +3565,41 @@ record_in_backtrace (Lisp_Object function, Lisp_Object *args, ptrdiff_t nargs) | |||
| 3564 | state. | 3565 | state. |
| 3565 | 3566 | ||
| 3566 | Members are volatile if their values need to survive _longjmp when | 3567 | Members are volatile if their values need to survive _longjmp when |
| 3567 | a 'struct handler' is a local variable. */ | 3568 | a 'struct handler' is a local variable. |
| 3568 | 3569 | ||
| 3569 | enum handlertype { CATCHER, CONDITION_CASE, CATCHER_ALL }; | 3570 | When running the HANDLER of a 'handler-bind', we need to |
| 3571 | temporarily "mute" the CONDITION_CASEs and HANDLERs that are "below" | ||
| 3572 | the current handler, but without hiding any CATCHERs. We do that by | ||
| 3573 | installing a SKIP_CONDITIONS which tells the search to skip the | ||
| 3574 | N next conditions. */ | ||
| 3575 | |||
| 3576 | enum handlertype { | ||
| 3577 | CATCHER, /* Entry for 'catch'. | ||
| 3578 | 'tag_or_ch' holds the catch's tag. | ||
| 3579 | 'val' holds the retval during longjmp. */ | ||
| 3580 | CONDITION_CASE, /* Entry for 'condition-case'. | ||
| 3581 | 'tag_or_ch' holds the list of conditions. | ||
| 3582 | 'val' holds the retval during longjmp. */ | ||
| 3583 | CATCHER_ALL, /* Wildcard which catches all 'throw's. | ||
| 3584 | 'tag_or_ch' is unused. | ||
| 3585 | 'val' holds the retval during longjmp. */ | ||
| 3586 | HANDLER_BIND, /* Entry for 'handler-bind'. | ||
| 3587 | 'tag_or_ch' holds the list of conditions. | ||
| 3588 | 'val' holds the handler function. | ||
| 3589 | The rest of the handler is unused, | ||
| 3590 | except for 'bytecode_dest' that holds | ||
| 3591 | the number of preceding HANDLER_BIND | ||
| 3592 | entries which belong to the same | ||
| 3593 | 'handler-bind' (and hence need to | ||
| 3594 | be muted together). */ | ||
| 3595 | SKIP_CONDITIONS /* Mask out the N preceding entries. | ||
| 3596 | Used while running the handler of | ||
| 3597 | a HANDLER_BIND to hides the condition | ||
| 3598 | handlers underneath (and including) | ||
| 3599 | the 'handler-bind'. | ||
| 3600 | 'tag_or_ch' holds that number, the rest | ||
| 3601 | is unused. */ | ||
| 3602 | }; | ||
| 3570 | 3603 | ||
| 3571 | enum nonlocal_exit | 3604 | enum nonlocal_exit |
| 3572 | { | 3605 | { |