diff options
| author | Noam Postavsky | 2015-11-19 19:50:06 -0500 |
|---|---|---|
| committer | Noam Postavsky | 2016-12-02 20:25:14 -0500 |
| commit | 227213164e06363f0a4fb2beeeb647c99749299e (patch) | |
| tree | 8fda48112af0631ce9b6c595e33101a9b5961909 /src/data.c | |
| parent | 0fc4761ca88175c30da7209c9ab1cde788b66a76 (diff) | |
| download | emacs-227213164e06363f0a4fb2beeeb647c99749299e.tar.gz emacs-227213164e06363f0a4fb2beeeb647c99749299e.zip | |
Add lisp watchpoints
This allows calling a function whenever a symbol-value is changed.
* src/lisp.h (lisp_h_SYMBOL_TRAPPED_WRITE_P):
(SYMBOL_TRAPPED_WRITE_P): New function/macro.
(lisp_h_SYMBOL_CONSTANT_P): Check for SYMBOL_NOWRITE specifically.
(enum symbol_trapped_write): New enumeration.
(struct Lisp_Symbol): Rename field constant to trapped_write.
(make_symbol_constant): New function.
* src/data.c (Fadd_variable_watcher, Fremove_variable_watcher):
(set_symbol_trapped_write, restore_symbol_trapped_write):
(harmonize_variable_watchers, notify_variable_watchers): New functions.
* src/data.c (Fset_default): Call `notify_variable_watchers' for trapped
symbols.
(set_internal): Change bool argument BIND to 3-value enum and call
`notify_variable_watchers' for trapped symbols.
* src/data.c (syms_of_data):
* src/data.c (syms_of_data):
* src/font.c (syms_of_font):
* src/lread.c (intern_sym, init_obarray):
* src/buffer.c (syms_of_buffer): Use make_symbol_constant.
* src/alloc.c (init_symbol):
* src/bytecode.c (exec_byte_code): Use SYMBOL_TRAPPED_WRITE_P.
* src/data.c (Fmake_variable_buffer_local, Fmake_local_variable):
(Fmake_variable_frame_local):
* src/eval.c (Fdefvaralias, specbind): Refer to Lisp_Symbol's
trapped_write instead of constant.
(Ffuncall): Move subr calling code into separate function.
(funcall_subr): New function.
Diffstat (limited to 'src/data.c')
| -rw-r--r-- | src/data.c | 191 |
1 files changed, 169 insertions, 22 deletions
diff --git a/src/data.c b/src/data.c index 61b5da8b5b6..07730d0924f 100644 --- a/src/data.c +++ b/src/data.c | |||
| @@ -1225,7 +1225,7 @@ DEFUN ("set", Fset, Sset, 2, 2, 0, | |||
| 1225 | doc: /* Set SYMBOL's value to NEWVAL, and return NEWVAL. */) | 1225 | doc: /* Set SYMBOL's value to NEWVAL, and return NEWVAL. */) |
| 1226 | (register Lisp_Object symbol, Lisp_Object newval) | 1226 | (register Lisp_Object symbol, Lisp_Object newval) |
| 1227 | { | 1227 | { |
| 1228 | set_internal (symbol, newval, Qnil, 0); | 1228 | set_internal (symbol, newval, Qnil, SET_INTERNAL_SET); |
| 1229 | return newval; | 1229 | return newval; |
| 1230 | } | 1230 | } |
| 1231 | 1231 | ||
| @@ -1233,13 +1233,14 @@ DEFUN ("set", Fset, Sset, 2, 2, 0, | |||
| 1233 | If buffer/frame-locality is an issue, WHERE specifies which context to use. | 1233 | If buffer/frame-locality is an issue, WHERE specifies which context to use. |
| 1234 | (nil stands for the current buffer/frame). | 1234 | (nil stands for the current buffer/frame). |
| 1235 | 1235 | ||
| 1236 | If BINDFLAG is false, then if this symbol is supposed to become | 1236 | If BINDFLAG is SET_INTERNAL_SET, then if this symbol is supposed to |
| 1237 | local in every buffer where it is set, then we make it local. | 1237 | become local in every buffer where it is set, then we make it |
| 1238 | If BINDFLAG is true, we don't do that. */ | 1238 | local. If BINDFLAG is SET_INTERNAL_BIND or SET_INTERNAL_UNBIND, we |
| 1239 | don't do that. */ | ||
| 1239 | 1240 | ||
| 1240 | void | 1241 | void |
| 1241 | set_internal (Lisp_Object symbol, Lisp_Object newval, Lisp_Object where, | 1242 | set_internal (Lisp_Object symbol, Lisp_Object newval, Lisp_Object where, |
| 1242 | bool bindflag) | 1243 | enum Set_Internal_Bind bindflag) |
| 1243 | { | 1244 | { |
| 1244 | bool voide = EQ (newval, Qunbound); | 1245 | bool voide = EQ (newval, Qunbound); |
| 1245 | struct Lisp_Symbol *sym; | 1246 | struct Lisp_Symbol *sym; |
| @@ -1250,18 +1251,31 @@ set_internal (Lisp_Object symbol, Lisp_Object newval, Lisp_Object where, | |||
| 1250 | return; */ | 1251 | return; */ |
| 1251 | 1252 | ||
| 1252 | CHECK_SYMBOL (symbol); | 1253 | CHECK_SYMBOL (symbol); |
| 1253 | if (SYMBOL_CONSTANT_P (symbol)) | 1254 | sym = XSYMBOL (symbol); |
| 1255 | switch (sym->trapped_write) | ||
| 1254 | { | 1256 | { |
| 1257 | case SYMBOL_NOWRITE: | ||
| 1255 | if (NILP (Fkeywordp (symbol)) | 1258 | if (NILP (Fkeywordp (symbol)) |
| 1256 | || !EQ (newval, Fsymbol_value (symbol))) | 1259 | || !EQ (newval, Fsymbol_value (symbol))) |
| 1257 | xsignal1 (Qsetting_constant, symbol); | 1260 | xsignal1 (Qsetting_constant, symbol); |
| 1258 | else | 1261 | else |
| 1259 | /* Allow setting keywords to their own value. */ | 1262 | /* Allow setting keywords to their own value. */ |
| 1260 | return; | 1263 | return; |
| 1264 | |||
| 1265 | case SYMBOL_TRAPPED_WRITE: | ||
| 1266 | notify_variable_watchers (symbol, voide? Qnil : newval, | ||
| 1267 | (bindflag == SET_INTERNAL_BIND? Qlet : | ||
| 1268 | bindflag == SET_INTERNAL_UNBIND? Qunlet : | ||
| 1269 | voide? Qmakunbound : Qset), | ||
| 1270 | where); | ||
| 1271 | /* FALLTHROUGH! */ | ||
| 1272 | case SYMBOL_UNTRAPPED_WRITE: | ||
| 1273 | break; | ||
| 1274 | |||
| 1275 | default: emacs_abort (); | ||
| 1261 | } | 1276 | } |
| 1262 | 1277 | ||
| 1263 | maybe_set_redisplay (symbol); | 1278 | maybe_set_redisplay (symbol); |
| 1264 | sym = XSYMBOL (symbol); | ||
| 1265 | 1279 | ||
| 1266 | start: | 1280 | start: |
| 1267 | switch (sym->redirect) | 1281 | switch (sym->redirect) |
| @@ -1385,6 +1399,111 @@ set_internal (Lisp_Object symbol, Lisp_Object newval, Lisp_Object where, | |||
| 1385 | } | 1399 | } |
| 1386 | return; | 1400 | return; |
| 1387 | } | 1401 | } |
| 1402 | |||
| 1403 | static void | ||
| 1404 | set_symbol_trapped_write (Lisp_Object symbol, enum symbol_trapped_write trap) | ||
| 1405 | { | ||
| 1406 | struct Lisp_Symbol* sym = XSYMBOL (symbol); | ||
| 1407 | if (sym->trapped_write == SYMBOL_NOWRITE) | ||
| 1408 | xsignal1 (Qtrapping_constant, symbol); | ||
| 1409 | else if (sym->redirect == SYMBOL_LOCALIZED | ||
| 1410 | && SYMBOL_BLV (sym)->frame_local) | ||
| 1411 | xsignal1 (Qtrapping_frame_local, symbol); | ||
| 1412 | sym->trapped_write = trap; | ||
| 1413 | } | ||
| 1414 | |||
| 1415 | static void | ||
| 1416 | restore_symbol_trapped_write (Lisp_Object symbol) | ||
| 1417 | { | ||
| 1418 | set_symbol_trapped_write (symbol, SYMBOL_TRAPPED_WRITE); | ||
| 1419 | } | ||
| 1420 | |||
| 1421 | static void | ||
| 1422 | harmonize_variable_watchers (Lisp_Object alias, Lisp_Object base_variable) | ||
| 1423 | { | ||
| 1424 | if (!EQ (base_variable, alias) | ||
| 1425 | && EQ (base_variable, Findirect_variable (alias))) | ||
| 1426 | set_symbol_trapped_write | ||
| 1427 | (alias, XSYMBOL (base_variable)->trapped_write); | ||
| 1428 | } | ||
| 1429 | |||
| 1430 | DEFUN ("add-variable-watcher", Fadd_variable_watcher, Sadd_variable_watcher, | ||
| 1431 | 2, 2, 0, | ||
| 1432 | doc: /* Cause WATCH-FUNCTION to be called when SYMBOL is set. | ||
| 1433 | All writes to aliases of SYMBOL will call WATCH-FUNCTION too. */) | ||
| 1434 | (Lisp_Object symbol, Lisp_Object watch_function) | ||
| 1435 | { | ||
| 1436 | symbol = Findirect_variable (symbol); | ||
| 1437 | set_symbol_trapped_write (symbol, SYMBOL_TRAPPED_WRITE); | ||
| 1438 | map_obarray (Vobarray, harmonize_variable_watchers, symbol); | ||
| 1439 | |||
| 1440 | Lisp_Object watchers = Fget (symbol, Qwatchers); | ||
| 1441 | Lisp_Object member = Fmember (watch_function, watchers); | ||
| 1442 | if (NILP (member)) | ||
| 1443 | Fput (symbol, Qwatchers, Fcons (watch_function, watchers)); | ||
| 1444 | return Qnil; | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | DEFUN ("remove-variable-watcher", Fremove_variable_watcher, Sremove_variable_watcher, | ||
| 1448 | 2, 2, 0, | ||
| 1449 | doc: /* Undo the effect of `add-variable-watcher'. | ||
| 1450 | Remove WATCH-FUNCTION from the list of functions to be called when | ||
| 1451 | SYMBOL (or its aliases) are set. */) | ||
| 1452 | (Lisp_Object symbol, Lisp_Object watch_function) | ||
| 1453 | { | ||
| 1454 | symbol = Findirect_variable (symbol); | ||
| 1455 | Lisp_Object watchers = Fget (symbol, Qwatchers); | ||
| 1456 | watchers = Fdelete (watch_function, watchers); | ||
| 1457 | if (NILP (watchers)) | ||
| 1458 | { | ||
| 1459 | set_symbol_trapped_write (symbol, SYMBOL_UNTRAPPED_WRITE); | ||
| 1460 | map_obarray (Vobarray, harmonize_variable_watchers, symbol); | ||
| 1461 | } | ||
| 1462 | Fput (symbol, Qwatchers, watchers); | ||
| 1463 | return Qnil; | ||
| 1464 | } | ||
| 1465 | |||
| 1466 | void | ||
| 1467 | notify_variable_watchers (Lisp_Object symbol, | ||
| 1468 | Lisp_Object newval, | ||
| 1469 | Lisp_Object operation, | ||
| 1470 | Lisp_Object where) | ||
| 1471 | { | ||
| 1472 | symbol = Findirect_variable (symbol); | ||
| 1473 | |||
| 1474 | ptrdiff_t count = SPECPDL_INDEX (); | ||
| 1475 | record_unwind_protect (restore_symbol_trapped_write, symbol); | ||
| 1476 | /* Avoid recursion. */ | ||
| 1477 | set_symbol_trapped_write (symbol, SYMBOL_UNTRAPPED_WRITE); | ||
| 1478 | |||
| 1479 | if (NILP (where) | ||
| 1480 | && !EQ (operation, Qset_default) && !EQ (operation, Qmakunbound) | ||
| 1481 | && !NILP (Flocal_variable_if_set_p (symbol, Fcurrent_buffer ()))) | ||
| 1482 | { | ||
| 1483 | XSETBUFFER (where, current_buffer); | ||
| 1484 | } | ||
| 1485 | |||
| 1486 | if (EQ (operation, Qset_default)) | ||
| 1487 | operation = Qset; | ||
| 1488 | |||
| 1489 | for (Lisp_Object watchers = Fget (symbol, Qwatchers); | ||
| 1490 | CONSP (watchers); | ||
| 1491 | watchers = XCDR (watchers)) | ||
| 1492 | { | ||
| 1493 | Lisp_Object watcher = XCAR (watchers); | ||
| 1494 | /* Call subr directly to avoid gc. */ | ||
| 1495 | if (SUBRP (watcher)) | ||
| 1496 | { | ||
| 1497 | Lisp_Object args[] = { symbol, newval, operation, where }; | ||
| 1498 | funcall_subr (XSUBR (watcher), ARRAYELTS (args), args); | ||
| 1499 | } | ||
| 1500 | else | ||
| 1501 | CALLN (Ffuncall, watcher, symbol, newval, operation, where); | ||
| 1502 | } | ||
| 1503 | |||
| 1504 | unbind_to (count, Qnil); | ||
| 1505 | } | ||
| 1506 | |||
| 1388 | 1507 | ||
| 1389 | /* Access or set a buffer-local symbol's default value. */ | 1508 | /* Access or set a buffer-local symbol's default value. */ |
| 1390 | 1509 | ||
| @@ -1471,16 +1590,27 @@ for this variable. */) | |||
| 1471 | struct Lisp_Symbol *sym; | 1590 | struct Lisp_Symbol *sym; |
| 1472 | 1591 | ||
| 1473 | CHECK_SYMBOL (symbol); | 1592 | CHECK_SYMBOL (symbol); |
| 1474 | if (SYMBOL_CONSTANT_P (symbol)) | 1593 | sym = XSYMBOL (symbol); |
| 1594 | switch (sym->trapped_write) | ||
| 1475 | { | 1595 | { |
| 1596 | case SYMBOL_NOWRITE: | ||
| 1476 | if (NILP (Fkeywordp (symbol)) | 1597 | if (NILP (Fkeywordp (symbol)) |
| 1477 | || !EQ (value, Fdefault_value (symbol))) | 1598 | || !EQ (value, Fsymbol_value (symbol))) |
| 1478 | xsignal1 (Qsetting_constant, symbol); | 1599 | xsignal1 (Qsetting_constant, symbol); |
| 1479 | else | 1600 | else |
| 1480 | /* Allow setting keywords to their own value. */ | 1601 | /* Allow setting keywords to their own value. */ |
| 1481 | return value; | 1602 | return value; |
| 1603 | |||
| 1604 | case SYMBOL_TRAPPED_WRITE: | ||
| 1605 | /* Don't notify here if we're going to call Fset anyway. */ | ||
| 1606 | if (sym->redirect != SYMBOL_PLAINVAL) | ||
| 1607 | notify_variable_watchers (symbol, value, Qset_default, Qnil); | ||
| 1608 | /* FALLTHROUGH! */ | ||
| 1609 | case SYMBOL_UNTRAPPED_WRITE: | ||
| 1610 | break; | ||
| 1611 | |||
| 1612 | default: emacs_abort (); | ||
| 1482 | } | 1613 | } |
| 1483 | sym = XSYMBOL (symbol); | ||
| 1484 | 1614 | ||
| 1485 | start: | 1615 | start: |
| 1486 | switch (sym->redirect) | 1616 | switch (sym->redirect) |
| @@ -1651,7 +1781,7 @@ The function `default-value' gets the default value and `set-default' sets it. | |||
| 1651 | default: emacs_abort (); | 1781 | default: emacs_abort (); |
| 1652 | } | 1782 | } |
| 1653 | 1783 | ||
| 1654 | if (sym->constant) | 1784 | if (SYMBOL_CONSTANT_P (variable)) |
| 1655 | error ("Symbol %s may not be buffer-local", SDATA (SYMBOL_NAME (variable))); | 1785 | error ("Symbol %s may not be buffer-local", SDATA (SYMBOL_NAME (variable))); |
| 1656 | 1786 | ||
| 1657 | if (!blv) | 1787 | if (!blv) |
| @@ -1726,7 +1856,7 @@ Instead, use `add-hook' and specify t for the LOCAL argument. */) | |||
| 1726 | default: emacs_abort (); | 1856 | default: emacs_abort (); |
| 1727 | } | 1857 | } |
| 1728 | 1858 | ||
| 1729 | if (sym->constant) | 1859 | if (sym->trapped_write == SYMBOL_NOWRITE) |
| 1730 | error ("Symbol %s may not be buffer-local", | 1860 | error ("Symbol %s may not be buffer-local", |
| 1731 | SDATA (SYMBOL_NAME (variable))); | 1861 | SDATA (SYMBOL_NAME (variable))); |
| 1732 | 1862 | ||
| @@ -1838,6 +1968,9 @@ From now on the default value will apply in this buffer. Return VARIABLE. */) | |||
| 1838 | default: emacs_abort (); | 1968 | default: emacs_abort (); |
| 1839 | } | 1969 | } |
| 1840 | 1970 | ||
| 1971 | if (sym->trapped_write == SYMBOL_TRAPPED_WRITE) | ||
| 1972 | notify_variable_watchers (variable, Qnil, Qmakunbound, Fcurrent_buffer ()); | ||
| 1973 | |||
| 1841 | /* Get rid of this buffer's alist element, if any. */ | 1974 | /* Get rid of this buffer's alist element, if any. */ |
| 1842 | XSETSYMBOL (variable, sym); /* Propagate variable indirection. */ | 1975 | XSETSYMBOL (variable, sym); /* Propagate variable indirection. */ |
| 1843 | tem = Fassq (variable, BVAR (current_buffer, local_var_alist)); | 1976 | tem = Fassq (variable, BVAR (current_buffer, local_var_alist)); |
| @@ -1920,7 +2053,7 @@ frame-local bindings). */) | |||
| 1920 | default: emacs_abort (); | 2053 | default: emacs_abort (); |
| 1921 | } | 2054 | } |
| 1922 | 2055 | ||
| 1923 | if (sym->constant) | 2056 | if (SYMBOL_TRAPPED_WRITE_P (variable)) |
| 1924 | error ("Symbol %s may not be frame-local", SDATA (SYMBOL_NAME (variable))); | 2057 | error ("Symbol %s may not be frame-local", SDATA (SYMBOL_NAME (variable))); |
| 1925 | 2058 | ||
| 1926 | blv = make_blv (sym, forwarded, valcontents); | 2059 | blv = make_blv (sym, forwarded, valcontents); |
| @@ -3465,6 +3598,8 @@ syms_of_data (void) | |||
| 3465 | DEFSYM (Qcyclic_variable_indirection, "cyclic-variable-indirection"); | 3598 | DEFSYM (Qcyclic_variable_indirection, "cyclic-variable-indirection"); |
| 3466 | DEFSYM (Qvoid_variable, "void-variable"); | 3599 | DEFSYM (Qvoid_variable, "void-variable"); |
| 3467 | DEFSYM (Qsetting_constant, "setting-constant"); | 3600 | DEFSYM (Qsetting_constant, "setting-constant"); |
| 3601 | DEFSYM (Qtrapping_constant, "trapping-constant"); | ||
| 3602 | DEFSYM (Qtrapping_frame_local, "trapping-frame-local"); | ||
| 3468 | DEFSYM (Qinvalid_read_syntax, "invalid-read-syntax"); | 3603 | DEFSYM (Qinvalid_read_syntax, "invalid-read-syntax"); |
| 3469 | 3604 | ||
| 3470 | DEFSYM (Qinvalid_function, "invalid-function"); | 3605 | DEFSYM (Qinvalid_function, "invalid-function"); |
| @@ -3543,6 +3678,10 @@ syms_of_data (void) | |||
| 3543 | PUT_ERROR (Qvoid_variable, error_tail, "Symbol's value as variable is void"); | 3678 | PUT_ERROR (Qvoid_variable, error_tail, "Symbol's value as variable is void"); |
| 3544 | PUT_ERROR (Qsetting_constant, error_tail, | 3679 | PUT_ERROR (Qsetting_constant, error_tail, |
| 3545 | "Attempt to set a constant symbol"); | 3680 | "Attempt to set a constant symbol"); |
| 3681 | PUT_ERROR (Qtrapping_constant, error_tail, | ||
| 3682 | "Attempt to trap writes to a constant symbol"); | ||
| 3683 | PUT_ERROR (Qtrapping_frame_local, error_tail, | ||
| 3684 | "Attempt to trap writes to a frame local variable"); | ||
| 3546 | PUT_ERROR (Qinvalid_read_syntax, error_tail, "Invalid read syntax"); | 3685 | PUT_ERROR (Qinvalid_read_syntax, error_tail, "Invalid read syntax"); |
| 3547 | PUT_ERROR (Qinvalid_function, error_tail, "Invalid function"); | 3686 | PUT_ERROR (Qinvalid_function, error_tail, "Invalid function"); |
| 3548 | PUT_ERROR (Qwrong_number_of_arguments, error_tail, | 3687 | PUT_ERROR (Qwrong_number_of_arguments, error_tail, |
| @@ -3721,10 +3860,18 @@ syms_of_data (void) | |||
| 3721 | DEFVAR_LISP ("most-positive-fixnum", Vmost_positive_fixnum, | 3860 | DEFVAR_LISP ("most-positive-fixnum", Vmost_positive_fixnum, |
| 3722 | doc: /* The largest value that is representable in a Lisp integer. */); | 3861 | doc: /* The largest value that is representable in a Lisp integer. */); |
| 3723 | Vmost_positive_fixnum = make_number (MOST_POSITIVE_FIXNUM); | 3862 | Vmost_positive_fixnum = make_number (MOST_POSITIVE_FIXNUM); |
| 3724 | XSYMBOL (intern_c_string ("most-positive-fixnum"))->constant = 1; | 3863 | make_symbol_constant (intern_c_string ("most-positive-fixnum")); |
| 3725 | 3864 | ||
| 3726 | DEFVAR_LISP ("most-negative-fixnum", Vmost_negative_fixnum, | 3865 | DEFVAR_LISP ("most-negative-fixnum", Vmost_negative_fixnum, |
| 3727 | doc: /* The smallest value that is representable in a Lisp integer. */); | 3866 | doc: /* The smallest value that is representable in a Lisp integer. */); |
| 3728 | Vmost_negative_fixnum = make_number (MOST_NEGATIVE_FIXNUM); | 3867 | Vmost_negative_fixnum = make_number (MOST_NEGATIVE_FIXNUM); |
| 3729 | XSYMBOL (intern_c_string ("most-negative-fixnum"))->constant = 1; | 3868 | make_symbol_constant (intern_c_string ("most-negative-fixnum")); |
| 3869 | |||
| 3870 | DEFSYM (Qwatchers, "watchers"); | ||
| 3871 | DEFSYM (Qmakunbound, "makunbound"); | ||
| 3872 | DEFSYM (Qunlet, "unlet"); | ||
| 3873 | DEFSYM (Qset, "set"); | ||
| 3874 | DEFSYM (Qset_default, "set-default"); | ||
| 3875 | defsubr (&Sadd_variable_watcher); | ||
| 3876 | defsubr (&Sremove_variable_watcher); | ||
| 3730 | } | 3877 | } |