diff options
Diffstat (limited to 'src/data.c')
| -rw-r--r-- | src/data.c | 213 |
1 files changed, 189 insertions, 24 deletions
diff --git a/src/data.c b/src/data.c index 61b5da8b5b6..eee2a52a37a 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,29 @@ 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; |
| 1261 | } | 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; | ||
| 1262 | 1274 | ||
| 1263 | maybe_set_redisplay (symbol); | 1275 | default: emacs_abort (); |
| 1264 | sym = XSYMBOL (symbol); | 1276 | } |
| 1265 | 1277 | ||
| 1266 | start: | 1278 | start: |
| 1267 | switch (sym->redirect) | 1279 | switch (sym->redirect) |
| @@ -1385,6 +1397,130 @@ set_internal (Lisp_Object symbol, Lisp_Object newval, Lisp_Object where, | |||
| 1385 | } | 1397 | } |
| 1386 | return; | 1398 | return; |
| 1387 | } | 1399 | } |
| 1400 | |||
| 1401 | static void | ||
| 1402 | set_symbol_trapped_write (Lisp_Object symbol, enum symbol_trapped_write trap) | ||
| 1403 | { | ||
| 1404 | struct Lisp_Symbol* sym = XSYMBOL (symbol); | ||
| 1405 | if (sym->trapped_write == SYMBOL_NOWRITE) | ||
| 1406 | xsignal1 (Qtrapping_constant, symbol); | ||
| 1407 | else if (sym->redirect == SYMBOL_LOCALIZED | ||
| 1408 | && SYMBOL_BLV (sym)->frame_local) | ||
| 1409 | xsignal1 (Qtrapping_frame_local, symbol); | ||
| 1410 | sym->trapped_write = trap; | ||
| 1411 | } | ||
| 1412 | |||
| 1413 | static void | ||
| 1414 | restore_symbol_trapped_write (Lisp_Object symbol) | ||
| 1415 | { | ||
| 1416 | set_symbol_trapped_write (symbol, SYMBOL_TRAPPED_WRITE); | ||
| 1417 | } | ||
| 1418 | |||
| 1419 | static void | ||
| 1420 | harmonize_variable_watchers (Lisp_Object alias, Lisp_Object base_variable) | ||
| 1421 | { | ||
| 1422 | if (!EQ (base_variable, alias) | ||
| 1423 | && EQ (base_variable, Findirect_variable (alias))) | ||
| 1424 | set_symbol_trapped_write | ||
| 1425 | (alias, XSYMBOL (base_variable)->trapped_write); | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | DEFUN ("add-variable-watcher", Fadd_variable_watcher, Sadd_variable_watcher, | ||
| 1429 | 2, 2, 0, | ||
| 1430 | doc: /* Cause WATCH-FUNCTION to be called when SYMBOL is set. | ||
| 1431 | |||
| 1432 | It will be called with 4 arguments: (SYMBOL NEWVAL OPERATION WHERE). | ||
| 1433 | SYMBOL is the variable being changed. | ||
| 1434 | NEWVAL is the value it will be changed to. | ||
| 1435 | OPERATION is a symbol representing the kind of change, one of: `set', | ||
| 1436 | `let', `unlet', `makunbound', and `defvaralias'. | ||
| 1437 | WHERE is a buffer if the buffer-local value of the variable being | ||
| 1438 | changed, nil otherwise. | ||
| 1439 | |||
| 1440 | All writes to aliases of SYMBOL will call WATCH-FUNCTION too. */) | ||
| 1441 | (Lisp_Object symbol, Lisp_Object watch_function) | ||
| 1442 | { | ||
| 1443 | symbol = Findirect_variable (symbol); | ||
| 1444 | set_symbol_trapped_write (symbol, SYMBOL_TRAPPED_WRITE); | ||
| 1445 | map_obarray (Vobarray, harmonize_variable_watchers, symbol); | ||
| 1446 | |||
| 1447 | Lisp_Object watchers = Fget (symbol, Qwatchers); | ||
| 1448 | Lisp_Object member = Fmember (watch_function, watchers); | ||
| 1449 | if (NILP (member)) | ||
| 1450 | Fput (symbol, Qwatchers, Fcons (watch_function, watchers)); | ||
| 1451 | return Qnil; | ||
| 1452 | } | ||
| 1453 | |||
| 1454 | DEFUN ("remove-variable-watcher", Fremove_variable_watcher, Sremove_variable_watcher, | ||
| 1455 | 2, 2, 0, | ||
| 1456 | doc: /* Undo the effect of `add-variable-watcher'. | ||
| 1457 | Remove WATCH-FUNCTION from the list of functions to be called when | ||
| 1458 | SYMBOL (or its aliases) are set. */) | ||
| 1459 | (Lisp_Object symbol, Lisp_Object watch_function) | ||
| 1460 | { | ||
| 1461 | symbol = Findirect_variable (symbol); | ||
| 1462 | Lisp_Object watchers = Fget (symbol, Qwatchers); | ||
| 1463 | watchers = Fdelete (watch_function, watchers); | ||
| 1464 | if (NILP (watchers)) | ||
| 1465 | { | ||
| 1466 | set_symbol_trapped_write (symbol, SYMBOL_UNTRAPPED_WRITE); | ||
| 1467 | map_obarray (Vobarray, harmonize_variable_watchers, symbol); | ||
| 1468 | } | ||
| 1469 | Fput (symbol, Qwatchers, watchers); | ||
| 1470 | return Qnil; | ||
| 1471 | } | ||
| 1472 | |||
| 1473 | DEFUN ("get-variable-watchers", Fget_variable_watchers, Sget_variable_watchers, | ||
| 1474 | 1, 1, 0, | ||
| 1475 | doc: /* Return a list of SYMBOL's active watchers. */) | ||
| 1476 | (Lisp_Object symbol) | ||
| 1477 | { | ||
| 1478 | return (SYMBOL_TRAPPED_WRITE_P (symbol) == SYMBOL_TRAPPED_WRITE) | ||
| 1479 | ? Fget (Findirect_variable (symbol), Qwatchers) | ||
| 1480 | : Qnil; | ||
| 1481 | } | ||
| 1482 | |||
| 1483 | void | ||
| 1484 | notify_variable_watchers (Lisp_Object symbol, | ||
| 1485 | Lisp_Object newval, | ||
| 1486 | Lisp_Object operation, | ||
| 1487 | Lisp_Object where) | ||
| 1488 | { | ||
| 1489 | symbol = Findirect_variable (symbol); | ||
| 1490 | |||
| 1491 | ptrdiff_t count = SPECPDL_INDEX (); | ||
| 1492 | record_unwind_protect (restore_symbol_trapped_write, symbol); | ||
| 1493 | /* Avoid recursion. */ | ||
| 1494 | set_symbol_trapped_write (symbol, SYMBOL_UNTRAPPED_WRITE); | ||
| 1495 | |||
| 1496 | if (NILP (where) | ||
| 1497 | && !EQ (operation, Qset_default) && !EQ (operation, Qmakunbound) | ||
| 1498 | && !NILP (Flocal_variable_if_set_p (symbol, Fcurrent_buffer ()))) | ||
| 1499 | { | ||
| 1500 | XSETBUFFER (where, current_buffer); | ||
| 1501 | } | ||
| 1502 | |||
| 1503 | if (EQ (operation, Qset_default)) | ||
| 1504 | operation = Qset; | ||
| 1505 | |||
| 1506 | for (Lisp_Object watchers = Fget (symbol, Qwatchers); | ||
| 1507 | CONSP (watchers); | ||
| 1508 | watchers = XCDR (watchers)) | ||
| 1509 | { | ||
| 1510 | Lisp_Object watcher = XCAR (watchers); | ||
| 1511 | /* Call subr directly to avoid gc. */ | ||
| 1512 | if (SUBRP (watcher)) | ||
| 1513 | { | ||
| 1514 | Lisp_Object args[] = { symbol, newval, operation, where }; | ||
| 1515 | funcall_subr (XSUBR (watcher), ARRAYELTS (args), args); | ||
| 1516 | } | ||
| 1517 | else | ||
| 1518 | CALLN (Ffuncall, watcher, symbol, newval, operation, where); | ||
| 1519 | } | ||
| 1520 | |||
| 1521 | unbind_to (count, Qnil); | ||
| 1522 | } | ||
| 1523 | |||
| 1388 | 1524 | ||
| 1389 | /* Access or set a buffer-local symbol's default value. */ | 1525 | /* Access or set a buffer-local symbol's default value. */ |
| 1390 | 1526 | ||
| @@ -1471,16 +1607,27 @@ for this variable. */) | |||
| 1471 | struct Lisp_Symbol *sym; | 1607 | struct Lisp_Symbol *sym; |
| 1472 | 1608 | ||
| 1473 | CHECK_SYMBOL (symbol); | 1609 | CHECK_SYMBOL (symbol); |
| 1474 | if (SYMBOL_CONSTANT_P (symbol)) | 1610 | sym = XSYMBOL (symbol); |
| 1611 | switch (sym->trapped_write) | ||
| 1475 | { | 1612 | { |
| 1613 | case SYMBOL_NOWRITE: | ||
| 1476 | if (NILP (Fkeywordp (symbol)) | 1614 | if (NILP (Fkeywordp (symbol)) |
| 1477 | || !EQ (value, Fdefault_value (symbol))) | 1615 | || !EQ (value, Fsymbol_value (symbol))) |
| 1478 | xsignal1 (Qsetting_constant, symbol); | 1616 | xsignal1 (Qsetting_constant, symbol); |
| 1479 | else | 1617 | else |
| 1480 | /* Allow setting keywords to their own value. */ | 1618 | /* Allow setting keywords to their own value. */ |
| 1481 | return value; | 1619 | return value; |
| 1620 | |||
| 1621 | case SYMBOL_TRAPPED_WRITE: | ||
| 1622 | /* Don't notify here if we're going to call Fset anyway. */ | ||
| 1623 | if (sym->redirect != SYMBOL_PLAINVAL) | ||
| 1624 | notify_variable_watchers (symbol, value, Qset_default, Qnil); | ||
| 1625 | /* FALLTHROUGH! */ | ||
| 1626 | case SYMBOL_UNTRAPPED_WRITE: | ||
| 1627 | break; | ||
| 1628 | |||
| 1629 | default: emacs_abort (); | ||
| 1482 | } | 1630 | } |
| 1483 | sym = XSYMBOL (symbol); | ||
| 1484 | 1631 | ||
| 1485 | start: | 1632 | start: |
| 1486 | switch (sym->redirect) | 1633 | switch (sym->redirect) |
| @@ -1651,7 +1798,7 @@ The function `default-value' gets the default value and `set-default' sets it. | |||
| 1651 | default: emacs_abort (); | 1798 | default: emacs_abort (); |
| 1652 | } | 1799 | } |
| 1653 | 1800 | ||
| 1654 | if (sym->constant) | 1801 | if (SYMBOL_CONSTANT_P (variable)) |
| 1655 | error ("Symbol %s may not be buffer-local", SDATA (SYMBOL_NAME (variable))); | 1802 | error ("Symbol %s may not be buffer-local", SDATA (SYMBOL_NAME (variable))); |
| 1656 | 1803 | ||
| 1657 | if (!blv) | 1804 | if (!blv) |
| @@ -1726,7 +1873,7 @@ Instead, use `add-hook' and specify t for the LOCAL argument. */) | |||
| 1726 | default: emacs_abort (); | 1873 | default: emacs_abort (); |
| 1727 | } | 1874 | } |
| 1728 | 1875 | ||
| 1729 | if (sym->constant) | 1876 | if (sym->trapped_write == SYMBOL_NOWRITE) |
| 1730 | error ("Symbol %s may not be buffer-local", | 1877 | error ("Symbol %s may not be buffer-local", |
| 1731 | SDATA (SYMBOL_NAME (variable))); | 1878 | SDATA (SYMBOL_NAME (variable))); |
| 1732 | 1879 | ||
| @@ -1838,6 +1985,9 @@ From now on the default value will apply in this buffer. Return VARIABLE. */) | |||
| 1838 | default: emacs_abort (); | 1985 | default: emacs_abort (); |
| 1839 | } | 1986 | } |
| 1840 | 1987 | ||
| 1988 | if (sym->trapped_write == SYMBOL_TRAPPED_WRITE) | ||
| 1989 | notify_variable_watchers (variable, Qnil, Qmakunbound, Fcurrent_buffer ()); | ||
| 1990 | |||
| 1841 | /* Get rid of this buffer's alist element, if any. */ | 1991 | /* Get rid of this buffer's alist element, if any. */ |
| 1842 | XSETSYMBOL (variable, sym); /* Propagate variable indirection. */ | 1992 | XSETSYMBOL (variable, sym); /* Propagate variable indirection. */ |
| 1843 | tem = Fassq (variable, BVAR (current_buffer, local_var_alist)); | 1993 | tem = Fassq (variable, BVAR (current_buffer, local_var_alist)); |
| @@ -1920,7 +2070,7 @@ frame-local bindings). */) | |||
| 1920 | default: emacs_abort (); | 2070 | default: emacs_abort (); |
| 1921 | } | 2071 | } |
| 1922 | 2072 | ||
| 1923 | if (sym->constant) | 2073 | if (SYMBOL_TRAPPED_WRITE_P (variable)) |
| 1924 | error ("Symbol %s may not be frame-local", SDATA (SYMBOL_NAME (variable))); | 2074 | error ("Symbol %s may not be frame-local", SDATA (SYMBOL_NAME (variable))); |
| 1925 | 2075 | ||
| 1926 | blv = make_blv (sym, forwarded, valcontents); | 2076 | blv = make_blv (sym, forwarded, valcontents); |
| @@ -3465,6 +3615,8 @@ syms_of_data (void) | |||
| 3465 | DEFSYM (Qcyclic_variable_indirection, "cyclic-variable-indirection"); | 3615 | DEFSYM (Qcyclic_variable_indirection, "cyclic-variable-indirection"); |
| 3466 | DEFSYM (Qvoid_variable, "void-variable"); | 3616 | DEFSYM (Qvoid_variable, "void-variable"); |
| 3467 | DEFSYM (Qsetting_constant, "setting-constant"); | 3617 | DEFSYM (Qsetting_constant, "setting-constant"); |
| 3618 | DEFSYM (Qtrapping_constant, "trapping-constant"); | ||
| 3619 | DEFSYM (Qtrapping_frame_local, "trapping-frame-local"); | ||
| 3468 | DEFSYM (Qinvalid_read_syntax, "invalid-read-syntax"); | 3620 | DEFSYM (Qinvalid_read_syntax, "invalid-read-syntax"); |
| 3469 | 3621 | ||
| 3470 | DEFSYM (Qinvalid_function, "invalid-function"); | 3622 | DEFSYM (Qinvalid_function, "invalid-function"); |
| @@ -3543,6 +3695,10 @@ syms_of_data (void) | |||
| 3543 | PUT_ERROR (Qvoid_variable, error_tail, "Symbol's value as variable is void"); | 3695 | PUT_ERROR (Qvoid_variable, error_tail, "Symbol's value as variable is void"); |
| 3544 | PUT_ERROR (Qsetting_constant, error_tail, | 3696 | PUT_ERROR (Qsetting_constant, error_tail, |
| 3545 | "Attempt to set a constant symbol"); | 3697 | "Attempt to set a constant symbol"); |
| 3698 | PUT_ERROR (Qtrapping_constant, error_tail, | ||
| 3699 | "Attempt to trap writes to a constant symbol"); | ||
| 3700 | PUT_ERROR (Qtrapping_frame_local, error_tail, | ||
| 3701 | "Attempt to trap writes to a frame local variable"); | ||
| 3546 | PUT_ERROR (Qinvalid_read_syntax, error_tail, "Invalid read syntax"); | 3702 | PUT_ERROR (Qinvalid_read_syntax, error_tail, "Invalid read syntax"); |
| 3547 | PUT_ERROR (Qinvalid_function, error_tail, "Invalid function"); | 3703 | PUT_ERROR (Qinvalid_function, error_tail, "Invalid function"); |
| 3548 | PUT_ERROR (Qwrong_number_of_arguments, error_tail, | 3704 | PUT_ERROR (Qwrong_number_of_arguments, error_tail, |
| @@ -3721,10 +3877,19 @@ syms_of_data (void) | |||
| 3721 | DEFVAR_LISP ("most-positive-fixnum", Vmost_positive_fixnum, | 3877 | DEFVAR_LISP ("most-positive-fixnum", Vmost_positive_fixnum, |
| 3722 | doc: /* The largest value that is representable in a Lisp integer. */); | 3878 | doc: /* The largest value that is representable in a Lisp integer. */); |
| 3723 | Vmost_positive_fixnum = make_number (MOST_POSITIVE_FIXNUM); | 3879 | Vmost_positive_fixnum = make_number (MOST_POSITIVE_FIXNUM); |
| 3724 | XSYMBOL (intern_c_string ("most-positive-fixnum"))->constant = 1; | 3880 | make_symbol_constant (intern_c_string ("most-positive-fixnum")); |
| 3725 | 3881 | ||
| 3726 | DEFVAR_LISP ("most-negative-fixnum", Vmost_negative_fixnum, | 3882 | DEFVAR_LISP ("most-negative-fixnum", Vmost_negative_fixnum, |
| 3727 | doc: /* The smallest value that is representable in a Lisp integer. */); | 3883 | doc: /* The smallest value that is representable in a Lisp integer. */); |
| 3728 | Vmost_negative_fixnum = make_number (MOST_NEGATIVE_FIXNUM); | 3884 | Vmost_negative_fixnum = make_number (MOST_NEGATIVE_FIXNUM); |
| 3729 | XSYMBOL (intern_c_string ("most-negative-fixnum"))->constant = 1; | 3885 | make_symbol_constant (intern_c_string ("most-negative-fixnum")); |
| 3886 | |||
| 3887 | DEFSYM (Qwatchers, "watchers"); | ||
| 3888 | DEFSYM (Qmakunbound, "makunbound"); | ||
| 3889 | DEFSYM (Qunlet, "unlet"); | ||
| 3890 | DEFSYM (Qset, "set"); | ||
| 3891 | DEFSYM (Qset_default, "set-default"); | ||
| 3892 | defsubr (&Sadd_variable_watcher); | ||
| 3893 | defsubr (&Sremove_variable_watcher); | ||
| 3894 | defsubr (&Sget_variable_watchers); | ||
| 3730 | } | 3895 | } |