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 | |
| 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.
| -rw-r--r-- | src/alloc.c | 2 | ||||
| -rw-r--r-- | src/buffer.c | 82 | ||||
| -rw-r--r-- | src/bytecode.c | 4 | ||||
| -rw-r--r-- | src/data.c | 191 | ||||
| -rw-r--r-- | src/eval.c | 206 | ||||
| -rw-r--r-- | src/font.c | 6 | ||||
| -rw-r--r-- | src/lisp.h | 54 | ||||
| -rw-r--r-- | src/lread.c | 6 |
8 files changed, 378 insertions, 173 deletions
diff --git a/src/alloc.c b/src/alloc.c index ae32400708a..6eced7bab18 100644 --- a/src/alloc.c +++ b/src/alloc.c | |||
| @@ -3567,7 +3567,7 @@ init_symbol (Lisp_Object val, Lisp_Object name) | |||
| 3567 | set_symbol_next (val, NULL); | 3567 | set_symbol_next (val, NULL); |
| 3568 | p->gcmarkbit = false; | 3568 | p->gcmarkbit = false; |
| 3569 | p->interned = SYMBOL_UNINTERNED; | 3569 | p->interned = SYMBOL_UNINTERNED; |
| 3570 | p->constant = 0; | 3570 | p->trapped_write = SYMBOL_UNTRAPPED_WRITE; |
| 3571 | p->declared_special = false; | 3571 | p->declared_special = false; |
| 3572 | p->pinned = false; | 3572 | p->pinned = false; |
| 3573 | } | 3573 | } |
diff --git a/src/buffer.c b/src/buffer.c index aa556b75bc6..6815aa7f7ed 100644 --- a/src/buffer.c +++ b/src/buffer.c | |||
| @@ -984,40 +984,54 @@ reset_buffer_local_variables (struct buffer *b, bool permanent_too) | |||
| 984 | bset_local_var_alist (b, Qnil); | 984 | bset_local_var_alist (b, Qnil); |
| 985 | else | 985 | else |
| 986 | { | 986 | { |
| 987 | Lisp_Object tmp, prop, last = Qnil; | 987 | Lisp_Object tmp, last = Qnil; |
| 988 | for (tmp = BVAR (b, local_var_alist); CONSP (tmp); tmp = XCDR (tmp)) | 988 | for (tmp = BVAR (b, local_var_alist); CONSP (tmp); tmp = XCDR (tmp)) |
| 989 | if (!NILP (prop = Fget (XCAR (XCAR (tmp)), Qpermanent_local))) | 989 | { |
| 990 | { | 990 | Lisp_Object local_var = XCAR (XCAR (tmp)); |
| 991 | /* If permanent-local, keep it. */ | 991 | Lisp_Object prop = Fget (local_var, Qpermanent_local); |
| 992 | last = tmp; | 992 | |
| 993 | if (EQ (prop, Qpermanent_local_hook)) | 993 | if (!NILP (prop)) |
| 994 | { | 994 | { |
| 995 | /* This is a partially permanent hook variable. | 995 | /* If permanent-local, keep it. */ |
| 996 | Preserve only the elements that want to be preserved. */ | 996 | last = tmp; |
| 997 | Lisp_Object list, newlist; | 997 | if (EQ (prop, Qpermanent_local_hook)) |
| 998 | list = XCDR (XCAR (tmp)); | 998 | { |
| 999 | if (!CONSP (list)) | 999 | /* This is a partially permanent hook variable. |
| 1000 | newlist = list; | 1000 | Preserve only the elements that want to be preserved. */ |
| 1001 | else | 1001 | Lisp_Object list, newlist; |
| 1002 | for (newlist = Qnil; CONSP (list); list = XCDR (list)) | 1002 | list = XCDR (XCAR (tmp)); |
| 1003 | { | 1003 | if (!CONSP (list)) |
| 1004 | Lisp_Object elt = XCAR (list); | 1004 | newlist = list; |
| 1005 | /* Preserve element ELT if it's t, | 1005 | else |
| 1006 | if it is a function with a `permanent-local-hook' property, | 1006 | for (newlist = Qnil; CONSP (list); list = XCDR (list)) |
| 1007 | or if it's not a symbol. */ | 1007 | { |
| 1008 | if (! SYMBOLP (elt) | 1008 | Lisp_Object elt = XCAR (list); |
| 1009 | || EQ (elt, Qt) | 1009 | /* Preserve element ELT if it's t, |
| 1010 | || !NILP (Fget (elt, Qpermanent_local_hook))) | 1010 | if it is a function with a `permanent-local-hook' property, |
| 1011 | newlist = Fcons (elt, newlist); | 1011 | or if it's not a symbol. */ |
| 1012 | } | 1012 | if (! SYMBOLP (elt) |
| 1013 | XSETCDR (XCAR (tmp), Fnreverse (newlist)); | 1013 | || EQ (elt, Qt) |
| 1014 | } | 1014 | || !NILP (Fget (elt, Qpermanent_local_hook))) |
| 1015 | } | 1015 | newlist = Fcons (elt, newlist); |
| 1016 | /* Delete this local variable. */ | 1016 | } |
| 1017 | else if (NILP (last)) | 1017 | newlist = Fnreverse (newlist); |
| 1018 | bset_local_var_alist (b, XCDR (tmp)); | 1018 | if (XSYMBOL (local_var)->trapped_write == SYMBOL_TRAPPED_WRITE) |
| 1019 | else | 1019 | notify_variable_watchers (local_var, newlist, |
| 1020 | XSETCDR (last, XCDR (tmp)); | 1020 | Qmakunbound, Fcurrent_buffer ()); |
| 1021 | XSETCDR (XCAR (tmp), newlist); | ||
| 1022 | continue; /* Don't do variable write trapping twice. */ | ||
| 1023 | } | ||
| 1024 | } | ||
| 1025 | /* Delete this local variable. */ | ||
| 1026 | else if (NILP (last)) | ||
| 1027 | bset_local_var_alist (b, XCDR (tmp)); | ||
| 1028 | else | ||
| 1029 | XSETCDR (last, XCDR (tmp)); | ||
| 1030 | |||
| 1031 | if (XSYMBOL (local_var)->trapped_write == SYMBOL_TRAPPED_WRITE) | ||
| 1032 | notify_variable_watchers (local_var, Qnil, | ||
| 1033 | Qmakunbound, Fcurrent_buffer ()); | ||
| 1034 | } | ||
| 1021 | } | 1035 | } |
| 1022 | 1036 | ||
| 1023 | for (i = 0; i < last_per_buffer_idx; ++i) | 1037 | for (i = 0; i < last_per_buffer_idx; ++i) |
| @@ -5541,7 +5555,7 @@ file I/O and the behavior of various editing commands. | |||
| 5541 | This variable is buffer-local but you cannot set it directly; | 5555 | This variable is buffer-local but you cannot set it directly; |
| 5542 | use the function `set-buffer-multibyte' to change a buffer's representation. | 5556 | use the function `set-buffer-multibyte' to change a buffer's representation. |
| 5543 | See also Info node `(elisp)Text Representations'. */); | 5557 | See also Info node `(elisp)Text Representations'. */); |
| 5544 | XSYMBOL (intern_c_string ("enable-multibyte-characters"))->constant = 1; | 5558 | make_symbol_constant (intern_c_string ("enable-multibyte-characters")); |
| 5545 | 5559 | ||
| 5546 | DEFVAR_PER_BUFFER ("buffer-file-coding-system", | 5560 | DEFVAR_PER_BUFFER ("buffer-file-coding-system", |
| 5547 | &BVAR (current_buffer, buffer_file_coding_system), Qnil, | 5561 | &BVAR (current_buffer, buffer_file_coding_system), Qnil, |
diff --git a/src/bytecode.c b/src/bytecode.c index be39a81c5e9..868c0148d30 100644 --- a/src/bytecode.c +++ b/src/bytecode.c | |||
| @@ -569,10 +569,10 @@ exec_byte_code (Lisp_Object bytestr, Lisp_Object vector, Lisp_Object maxdepth, | |||
| 569 | if (SYMBOLP (sym) | 569 | if (SYMBOLP (sym) |
| 570 | && !EQ (val, Qunbound) | 570 | && !EQ (val, Qunbound) |
| 571 | && !XSYMBOL (sym)->redirect | 571 | && !XSYMBOL (sym)->redirect |
| 572 | && !SYMBOL_CONSTANT_P (sym)) | 572 | && !SYMBOL_TRAPPED_WRITE_P (sym)) |
| 573 | SET_SYMBOL_VAL (XSYMBOL (sym), val); | 573 | SET_SYMBOL_VAL (XSYMBOL (sym), val); |
| 574 | else | 574 | else |
| 575 | set_internal (sym, val, Qnil, false); | 575 | set_internal (sym, val, Qnil, SET_INTERNAL_SET); |
| 576 | } | 576 | } |
| 577 | NEXT; | 577 | NEXT; |
| 578 | 578 | ||
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 | } |
diff --git a/src/eval.c b/src/eval.c index bbc1518be54..724f0018a58 100644 --- a/src/eval.c +++ b/src/eval.c | |||
| @@ -593,12 +593,12 @@ The return value is BASE-VARIABLE. */) | |||
| 593 | CHECK_SYMBOL (new_alias); | 593 | CHECK_SYMBOL (new_alias); |
| 594 | CHECK_SYMBOL (base_variable); | 594 | CHECK_SYMBOL (base_variable); |
| 595 | 595 | ||
| 596 | sym = XSYMBOL (new_alias); | 596 | if (SYMBOL_CONSTANT_P (new_alias)) |
| 597 | 597 | /* Making it an alias effectively changes its value. */ | |
| 598 | if (sym->constant) | ||
| 599 | /* Not sure why, but why not? */ | ||
| 600 | error ("Cannot make a constant an alias"); | 598 | error ("Cannot make a constant an alias"); |
| 601 | 599 | ||
| 600 | sym = XSYMBOL (new_alias); | ||
| 601 | |||
| 602 | switch (sym->redirect) | 602 | switch (sym->redirect) |
| 603 | { | 603 | { |
| 604 | case SYMBOL_FORWARDED: | 604 | case SYMBOL_FORWARDED: |
| @@ -617,8 +617,8 @@ The return value is BASE-VARIABLE. */) | |||
| 617 | so that old-code that affects n_a before the aliasing is setup | 617 | so that old-code that affects n_a before the aliasing is setup |
| 618 | still works. */ | 618 | still works. */ |
| 619 | if (NILP (Fboundp (base_variable))) | 619 | if (NILP (Fboundp (base_variable))) |
| 620 | set_internal (base_variable, find_symbol_value (new_alias), Qnil, 1); | 620 | set_internal (base_variable, find_symbol_value (new_alias), |
| 621 | 621 | Qnil, SET_INTERNAL_BIND); | |
| 622 | { | 622 | { |
| 623 | union specbinding *p; | 623 | union specbinding *p; |
| 624 | 624 | ||
| @@ -628,11 +628,14 @@ The return value is BASE-VARIABLE. */) | |||
| 628 | error ("Don't know how to make a let-bound variable an alias"); | 628 | error ("Don't know how to make a let-bound variable an alias"); |
| 629 | } | 629 | } |
| 630 | 630 | ||
| 631 | if (sym->trapped_write == SYMBOL_TRAPPED_WRITE) | ||
| 632 | notify_variable_watchers (new_alias, base_variable, Qdefvaralias, Qnil); | ||
| 633 | |||
| 631 | sym->declared_special = 1; | 634 | sym->declared_special = 1; |
| 632 | XSYMBOL (base_variable)->declared_special = 1; | 635 | XSYMBOL (base_variable)->declared_special = 1; |
| 633 | sym->redirect = SYMBOL_VARALIAS; | 636 | sym->redirect = SYMBOL_VARALIAS; |
| 634 | SET_SYMBOL_ALIAS (sym, XSYMBOL (base_variable)); | 637 | SET_SYMBOL_ALIAS (sym, XSYMBOL (base_variable)); |
| 635 | sym->constant = SYMBOL_CONSTANT_P (base_variable); | 638 | sym->trapped_write = XSYMBOL (base_variable)->trapped_write; |
| 636 | LOADHIST_ATTACH (new_alias); | 639 | LOADHIST_ATTACH (new_alias); |
| 637 | /* Even if docstring is nil: remove old docstring. */ | 640 | /* Even if docstring is nil: remove old docstring. */ |
| 638 | Fput (new_alias, Qvariable_documentation, docstring); | 641 | Fput (new_alias, Qvariable_documentation, docstring); |
| @@ -2645,9 +2648,7 @@ usage: (funcall FUNCTION &rest ARGUMENTS) */) | |||
| 2645 | Lisp_Object fun, original_fun; | 2648 | Lisp_Object fun, original_fun; |
| 2646 | Lisp_Object funcar; | 2649 | Lisp_Object funcar; |
| 2647 | ptrdiff_t numargs = nargs - 1; | 2650 | ptrdiff_t numargs = nargs - 1; |
| 2648 | Lisp_Object lisp_numargs; | ||
| 2649 | Lisp_Object val; | 2651 | Lisp_Object val; |
| 2650 | Lisp_Object *internal_args; | ||
| 2651 | ptrdiff_t count; | 2652 | ptrdiff_t count; |
| 2652 | 2653 | ||
| 2653 | QUIT; | 2654 | QUIT; |
| @@ -2680,86 +2681,7 @@ usage: (funcall FUNCTION &rest ARGUMENTS) */) | |||
| 2680 | fun = indirect_function (fun); | 2681 | fun = indirect_function (fun); |
| 2681 | 2682 | ||
| 2682 | if (SUBRP (fun)) | 2683 | if (SUBRP (fun)) |
| 2683 | { | 2684 | val = funcall_subr (XSUBR (fun), numargs, args + 1); |
| 2684 | if (numargs < XSUBR (fun)->min_args | ||
| 2685 | || (XSUBR (fun)->max_args >= 0 && XSUBR (fun)->max_args < numargs)) | ||
| 2686 | { | ||
| 2687 | XSETFASTINT (lisp_numargs, numargs); | ||
| 2688 | xsignal2 (Qwrong_number_of_arguments, original_fun, lisp_numargs); | ||
| 2689 | } | ||
| 2690 | |||
| 2691 | else if (XSUBR (fun)->max_args == UNEVALLED) | ||
| 2692 | xsignal1 (Qinvalid_function, original_fun); | ||
| 2693 | |||
| 2694 | else if (XSUBR (fun)->max_args == MANY) | ||
| 2695 | val = (XSUBR (fun)->function.aMANY) (numargs, args + 1); | ||
| 2696 | else | ||
| 2697 | { | ||
| 2698 | Lisp_Object internal_argbuf[8]; | ||
| 2699 | if (XSUBR (fun)->max_args > numargs) | ||
| 2700 | { | ||
| 2701 | eassert (XSUBR (fun)->max_args <= ARRAYELTS (internal_argbuf)); | ||
| 2702 | internal_args = internal_argbuf; | ||
| 2703 | memcpy (internal_args, args + 1, numargs * word_size); | ||
| 2704 | memclear (internal_args + numargs, | ||
| 2705 | (XSUBR (fun)->max_args - numargs) * word_size); | ||
| 2706 | } | ||
| 2707 | else | ||
| 2708 | internal_args = args + 1; | ||
| 2709 | switch (XSUBR (fun)->max_args) | ||
| 2710 | { | ||
| 2711 | case 0: | ||
| 2712 | val = (XSUBR (fun)->function.a0 ()); | ||
| 2713 | break; | ||
| 2714 | case 1: | ||
| 2715 | val = (XSUBR (fun)->function.a1 (internal_args[0])); | ||
| 2716 | break; | ||
| 2717 | case 2: | ||
| 2718 | val = (XSUBR (fun)->function.a2 | ||
| 2719 | (internal_args[0], internal_args[1])); | ||
| 2720 | break; | ||
| 2721 | case 3: | ||
| 2722 | val = (XSUBR (fun)->function.a3 | ||
| 2723 | (internal_args[0], internal_args[1], internal_args[2])); | ||
| 2724 | break; | ||
| 2725 | case 4: | ||
| 2726 | val = (XSUBR (fun)->function.a4 | ||
| 2727 | (internal_args[0], internal_args[1], internal_args[2], | ||
| 2728 | internal_args[3])); | ||
| 2729 | break; | ||
| 2730 | case 5: | ||
| 2731 | val = (XSUBR (fun)->function.a5 | ||
| 2732 | (internal_args[0], internal_args[1], internal_args[2], | ||
| 2733 | internal_args[3], internal_args[4])); | ||
| 2734 | break; | ||
| 2735 | case 6: | ||
| 2736 | val = (XSUBR (fun)->function.a6 | ||
| 2737 | (internal_args[0], internal_args[1], internal_args[2], | ||
| 2738 | internal_args[3], internal_args[4], internal_args[5])); | ||
| 2739 | break; | ||
| 2740 | case 7: | ||
| 2741 | val = (XSUBR (fun)->function.a7 | ||
| 2742 | (internal_args[0], internal_args[1], internal_args[2], | ||
| 2743 | internal_args[3], internal_args[4], internal_args[5], | ||
| 2744 | internal_args[6])); | ||
| 2745 | break; | ||
| 2746 | |||
| 2747 | case 8: | ||
| 2748 | val = (XSUBR (fun)->function.a8 | ||
| 2749 | (internal_args[0], internal_args[1], internal_args[2], | ||
| 2750 | internal_args[3], internal_args[4], internal_args[5], | ||
| 2751 | internal_args[6], internal_args[7])); | ||
| 2752 | break; | ||
| 2753 | |||
| 2754 | default: | ||
| 2755 | |||
| 2756 | /* If a subr takes more than 8 arguments without using MANY | ||
| 2757 | or UNEVALLED, we need to extend this function to support it. | ||
| 2758 | Until this is done, there is no way to call the function. */ | ||
| 2759 | emacs_abort (); | ||
| 2760 | } | ||
| 2761 | } | ||
| 2762 | } | ||
| 2763 | else if (COMPILEDP (fun)) | 2685 | else if (COMPILEDP (fun)) |
| 2764 | val = funcall_lambda (fun, numargs, args + 1); | 2686 | val = funcall_lambda (fun, numargs, args + 1); |
| 2765 | else | 2687 | else |
| @@ -2791,6 +2713,89 @@ usage: (funcall FUNCTION &rest ARGUMENTS) */) | |||
| 2791 | return val; | 2713 | return val; |
| 2792 | } | 2714 | } |
| 2793 | 2715 | ||
| 2716 | |||
| 2717 | /* Apply a C subroutine SUBR to the NUMARGS evaluated arguments in ARG_VECTOR | ||
| 2718 | and return the result of evaluation. */ | ||
| 2719 | |||
| 2720 | Lisp_Object | ||
| 2721 | funcall_subr (struct Lisp_Subr *subr, ptrdiff_t numargs, Lisp_Object *args) | ||
| 2722 | { | ||
| 2723 | if (numargs < subr->min_args | ||
| 2724 | || (subr->max_args >= 0 && subr->max_args < numargs)) | ||
| 2725 | { | ||
| 2726 | Lisp_Object fun; | ||
| 2727 | XSETSUBR (fun, subr); | ||
| 2728 | xsignal2 (Qwrong_number_of_arguments, fun, make_number (numargs)); | ||
| 2729 | } | ||
| 2730 | |||
| 2731 | else if (subr->max_args == UNEVALLED) | ||
| 2732 | { | ||
| 2733 | Lisp_Object fun; | ||
| 2734 | XSETSUBR (fun, subr); | ||
| 2735 | xsignal1 (Qinvalid_function, fun); | ||
| 2736 | } | ||
| 2737 | |||
| 2738 | else if (subr->max_args == MANY) | ||
| 2739 | return (subr->function.aMANY) (numargs, args); | ||
| 2740 | else | ||
| 2741 | { | ||
| 2742 | Lisp_Object internal_argbuf[8]; | ||
| 2743 | Lisp_Object *internal_args; | ||
| 2744 | if (subr->max_args > numargs) | ||
| 2745 | { | ||
| 2746 | eassert (subr->max_args <= ARRAYELTS (internal_argbuf)); | ||
| 2747 | internal_args = internal_argbuf; | ||
| 2748 | memcpy (internal_args, args, numargs * word_size); | ||
| 2749 | memclear (internal_args + numargs, | ||
| 2750 | (subr->max_args - numargs) * word_size); | ||
| 2751 | } | ||
| 2752 | else | ||
| 2753 | internal_args = args; | ||
| 2754 | switch (subr->max_args) | ||
| 2755 | { | ||
| 2756 | case 0: | ||
| 2757 | return (subr->function.a0 ()); | ||
| 2758 | case 1: | ||
| 2759 | return (subr->function.a1 (internal_args[0])); | ||
| 2760 | case 2: | ||
| 2761 | return (subr->function.a2 | ||
| 2762 | (internal_args[0], internal_args[1])); | ||
| 2763 | case 3: | ||
| 2764 | return (subr->function.a3 | ||
| 2765 | (internal_args[0], internal_args[1], internal_args[2])); | ||
| 2766 | case 4: | ||
| 2767 | return (subr->function.a4 | ||
| 2768 | (internal_args[0], internal_args[1], internal_args[2], | ||
| 2769 | internal_args[3])); | ||
| 2770 | case 5: | ||
| 2771 | return (subr->function.a5 | ||
| 2772 | (internal_args[0], internal_args[1], internal_args[2], | ||
| 2773 | internal_args[3], internal_args[4])); | ||
| 2774 | case 6: | ||
| 2775 | return (subr->function.a6 | ||
| 2776 | (internal_args[0], internal_args[1], internal_args[2], | ||
| 2777 | internal_args[3], internal_args[4], internal_args[5])); | ||
| 2778 | case 7: | ||
| 2779 | return (subr->function.a7 | ||
| 2780 | (internal_args[0], internal_args[1], internal_args[2], | ||
| 2781 | internal_args[3], internal_args[4], internal_args[5], | ||
| 2782 | internal_args[6])); | ||
| 2783 | case 8: | ||
| 2784 | return (subr->function.a8 | ||
| 2785 | (internal_args[0], internal_args[1], internal_args[2], | ||
| 2786 | internal_args[3], internal_args[4], internal_args[5], | ||
| 2787 | internal_args[6], internal_args[7])); | ||
| 2788 | |||
| 2789 | default: | ||
| 2790 | |||
| 2791 | /* If a subr takes more than 8 arguments without using MANY | ||
| 2792 | or UNEVALLED, we need to extend this function to support it. | ||
| 2793 | Until this is done, there is no way to call the function. */ | ||
| 2794 | emacs_abort (); | ||
| 2795 | } | ||
| 2796 | } | ||
| 2797 | } | ||
| 2798 | |||
| 2794 | static Lisp_Object | 2799 | static Lisp_Object |
| 2795 | apply_lambda (Lisp_Object fun, Lisp_Object args, ptrdiff_t count) | 2800 | apply_lambda (Lisp_Object fun, Lisp_Object args, ptrdiff_t count) |
| 2796 | { | 2801 | { |
| @@ -3171,10 +3176,10 @@ specbind (Lisp_Object symbol, Lisp_Object value) | |||
| 3171 | specpdl_ptr->let.symbol = symbol; | 3176 | specpdl_ptr->let.symbol = symbol; |
| 3172 | specpdl_ptr->let.old_value = SYMBOL_VAL (sym); | 3177 | specpdl_ptr->let.old_value = SYMBOL_VAL (sym); |
| 3173 | grow_specpdl (); | 3178 | grow_specpdl (); |
| 3174 | if (!sym->constant) | 3179 | if (!sym->trapped_write) |
| 3175 | SET_SYMBOL_VAL (sym, value); | 3180 | SET_SYMBOL_VAL (sym, value); |
| 3176 | else | 3181 | else |
| 3177 | set_internal (symbol, value, Qnil, 1); | 3182 | set_internal (symbol, value, Qnil, SET_INTERNAL_BIND); |
| 3178 | break; | 3183 | break; |
| 3179 | case SYMBOL_LOCALIZED: | 3184 | case SYMBOL_LOCALIZED: |
| 3180 | if (SYMBOL_BLV (sym)->frame_local) | 3185 | if (SYMBOL_BLV (sym)->frame_local) |
| @@ -3214,7 +3219,7 @@ specbind (Lisp_Object symbol, Lisp_Object value) | |||
| 3214 | specpdl_ptr->let.kind = SPECPDL_LET; | 3219 | specpdl_ptr->let.kind = SPECPDL_LET; |
| 3215 | 3220 | ||
| 3216 | grow_specpdl (); | 3221 | grow_specpdl (); |
| 3217 | set_internal (symbol, value, Qnil, 1); | 3222 | set_internal (symbol, value, Qnil, SET_INTERNAL_BIND); |
| 3218 | break; | 3223 | break; |
| 3219 | } | 3224 | } |
| 3220 | default: emacs_abort (); | 3225 | default: emacs_abort (); |
| @@ -3341,14 +3346,16 @@ unbind_to (ptrdiff_t count, Lisp_Object value) | |||
| 3341 | case SPECPDL_BACKTRACE: | 3346 | case SPECPDL_BACKTRACE: |
| 3342 | break; | 3347 | break; |
| 3343 | case SPECPDL_LET: | 3348 | case SPECPDL_LET: |
| 3344 | { /* If variable has a trivial value (no forwarding), we can | 3349 | { /* If variable has a trivial value (no forwarding), and |
| 3345 | just set it. No need to check for constant symbols here, | 3350 | isn't trapped, we can just set it. */ |
| 3346 | since that was already done by specbind. */ | ||
| 3347 | Lisp_Object sym = specpdl_symbol (specpdl_ptr); | 3351 | Lisp_Object sym = specpdl_symbol (specpdl_ptr); |
| 3348 | if (SYMBOLP (sym) && XSYMBOL (sym)->redirect == SYMBOL_PLAINVAL) | 3352 | if (SYMBOLP (sym) && XSYMBOL (sym)->redirect == SYMBOL_PLAINVAL) |
| 3349 | { | 3353 | { |
| 3350 | SET_SYMBOL_VAL (XSYMBOL (sym), | 3354 | if (XSYMBOL (sym)->trapped_write == SYMBOL_UNTRAPPED_WRITE) |
| 3351 | specpdl_old_value (specpdl_ptr)); | 3355 | SET_SYMBOL_VAL (XSYMBOL (sym), specpdl_old_value (specpdl_ptr)); |
| 3356 | else | ||
| 3357 | set_internal (sym, specpdl_old_value (specpdl_ptr), | ||
| 3358 | Qnil, SET_INTERNAL_UNBIND); | ||
| 3352 | break; | 3359 | break; |
| 3353 | } | 3360 | } |
| 3354 | else | 3361 | else |
| @@ -3371,7 +3378,7 @@ unbind_to (ptrdiff_t count, Lisp_Object value) | |||
| 3371 | /* If this was a local binding, reset the value in the appropriate | 3378 | /* If this was a local binding, reset the value in the appropriate |
| 3372 | buffer, but only if that buffer's binding still exists. */ | 3379 | buffer, but only if that buffer's binding still exists. */ |
| 3373 | if (!NILP (Flocal_variable_p (symbol, where))) | 3380 | if (!NILP (Flocal_variable_p (symbol, where))) |
| 3374 | set_internal (symbol, old_value, where, 1); | 3381 | set_internal (symbol, old_value, where, SET_INTERNAL_UNBIND); |
| 3375 | } | 3382 | } |
| 3376 | break; | 3383 | break; |
| 3377 | } | 3384 | } |
| @@ -3596,7 +3603,7 @@ backtrace_eval_unrewind (int distance) | |||
| 3596 | { | 3603 | { |
| 3597 | set_specpdl_old_value | 3604 | set_specpdl_old_value |
| 3598 | (tmp, Fbuffer_local_value (symbol, where)); | 3605 | (tmp, Fbuffer_local_value (symbol, where)); |
| 3599 | set_internal (symbol, old_value, where, 1); | 3606 | set_internal (symbol, old_value, where, SET_INTERNAL_UNBIND); |
| 3600 | } | 3607 | } |
| 3601 | } | 3608 | } |
| 3602 | break; | 3609 | break; |
| @@ -3940,6 +3947,7 @@ alist of active lexical bindings. */); | |||
| 3940 | defsubr (&Sset_default_toplevel_value); | 3947 | defsubr (&Sset_default_toplevel_value); |
| 3941 | defsubr (&Sdefvar); | 3948 | defsubr (&Sdefvar); |
| 3942 | defsubr (&Sdefvaralias); | 3949 | defsubr (&Sdefvaralias); |
| 3950 | DEFSYM (Qdefvaralias, "defvaralias"); | ||
| 3943 | defsubr (&Sdefconst); | 3951 | defsubr (&Sdefconst); |
| 3944 | defsubr (&Smake_var_non_special); | 3952 | defsubr (&Smake_var_non_special); |
| 3945 | defsubr (&Slet); | 3953 | defsubr (&Slet); |
diff --git a/src/font.c b/src/font.c index 9fe7c26ea9c..36e71669453 100644 --- a/src/font.c +++ b/src/font.c | |||
| @@ -5415,19 +5415,19 @@ Each element has the form: | |||
| 5415 | [NUMERIC-VALUE SYMBOLIC-NAME ALIAS-NAME ...] | 5415 | [NUMERIC-VALUE SYMBOLIC-NAME ALIAS-NAME ...] |
| 5416 | NUMERIC-VALUE is an integer, and SYMBOLIC-NAME and ALIAS-NAME are symbols. */); | 5416 | NUMERIC-VALUE is an integer, and SYMBOLIC-NAME and ALIAS-NAME are symbols. */); |
| 5417 | Vfont_weight_table = BUILD_STYLE_TABLE (weight_table); | 5417 | Vfont_weight_table = BUILD_STYLE_TABLE (weight_table); |
| 5418 | XSYMBOL (intern_c_string ("font-weight-table"))->constant = 1; | 5418 | make_symbol_constant (intern_c_string ("font-weight-table")); |
| 5419 | 5419 | ||
| 5420 | DEFVAR_LISP_NOPRO ("font-slant-table", Vfont_slant_table, | 5420 | DEFVAR_LISP_NOPRO ("font-slant-table", Vfont_slant_table, |
| 5421 | doc: /* Vector of font slant symbols vs the corresponding numeric values. | 5421 | doc: /* Vector of font slant symbols vs the corresponding numeric values. |
| 5422 | See `font-weight-table' for the format of the vector. */); | 5422 | See `font-weight-table' for the format of the vector. */); |
| 5423 | Vfont_slant_table = BUILD_STYLE_TABLE (slant_table); | 5423 | Vfont_slant_table = BUILD_STYLE_TABLE (slant_table); |
| 5424 | XSYMBOL (intern_c_string ("font-slant-table"))->constant = 1; | 5424 | make_symbol_constant (intern_c_string ("font-slant-table")); |
| 5425 | 5425 | ||
| 5426 | DEFVAR_LISP_NOPRO ("font-width-table", Vfont_width_table, | 5426 | DEFVAR_LISP_NOPRO ("font-width-table", Vfont_width_table, |
| 5427 | doc: /* Alist of font width symbols vs the corresponding numeric values. | 5427 | doc: /* Alist of font width symbols vs the corresponding numeric values. |
| 5428 | See `font-weight-table' for the format of the vector. */); | 5428 | See `font-weight-table' for the format of the vector. */); |
| 5429 | Vfont_width_table = BUILD_STYLE_TABLE (width_table); | 5429 | Vfont_width_table = BUILD_STYLE_TABLE (width_table); |
| 5430 | XSYMBOL (intern_c_string ("font-width-table"))->constant = 1; | 5430 | make_symbol_constant (intern_c_string ("font-width-table")); |
| 5431 | 5431 | ||
| 5432 | staticpro (&font_style_table); | 5432 | staticpro (&font_style_table); |
| 5433 | font_style_table = make_uninit_vector (3); | 5433 | font_style_table = make_uninit_vector (3); |
diff --git a/src/lisp.h b/src/lisp.h index b6c46687b28..94f1152a56e 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -320,7 +320,8 @@ error !; | |||
| 320 | #define lisp_h_NILP(x) EQ (x, Qnil) | 320 | #define lisp_h_NILP(x) EQ (x, Qnil) |
| 321 | #define lisp_h_SET_SYMBOL_VAL(sym, v) \ | 321 | #define lisp_h_SET_SYMBOL_VAL(sym, v) \ |
| 322 | (eassert ((sym)->redirect == SYMBOL_PLAINVAL), (sym)->val.value = (v)) | 322 | (eassert ((sym)->redirect == SYMBOL_PLAINVAL), (sym)->val.value = (v)) |
| 323 | #define lisp_h_SYMBOL_CONSTANT_P(sym) (XSYMBOL (sym)->constant) | 323 | #define lisp_h_SYMBOL_CONSTANT_P(sym) (XSYMBOL (sym)->trapped_write == SYMBOL_NOWRITE) |
| 324 | #define lisp_h_SYMBOL_TRAPPED_WRITE_P(sym) (XSYMBOL (sym)->trapped_write) | ||
| 324 | #define lisp_h_SYMBOL_VAL(sym) \ | 325 | #define lisp_h_SYMBOL_VAL(sym) \ |
| 325 | (eassert ((sym)->redirect == SYMBOL_PLAINVAL), (sym)->val.value) | 326 | (eassert ((sym)->redirect == SYMBOL_PLAINVAL), (sym)->val.value) |
| 326 | #define lisp_h_SYMBOLP(x) (XTYPE (x) == Lisp_Symbol) | 327 | #define lisp_h_SYMBOLP(x) (XTYPE (x) == Lisp_Symbol) |
| @@ -375,6 +376,7 @@ error !; | |||
| 375 | # define NILP(x) lisp_h_NILP (x) | 376 | # define NILP(x) lisp_h_NILP (x) |
| 376 | # define SET_SYMBOL_VAL(sym, v) lisp_h_SET_SYMBOL_VAL (sym, v) | 377 | # define SET_SYMBOL_VAL(sym, v) lisp_h_SET_SYMBOL_VAL (sym, v) |
| 377 | # define SYMBOL_CONSTANT_P(sym) lisp_h_SYMBOL_CONSTANT_P (sym) | 378 | # define SYMBOL_CONSTANT_P(sym) lisp_h_SYMBOL_CONSTANT_P (sym) |
| 379 | # define SYMBOL_TRAPPED_WRITE_P(sym) lisp_h_SYMBOL_TRAPPED_WRITE_P (sym) | ||
| 378 | # define SYMBOL_VAL(sym) lisp_h_SYMBOL_VAL (sym) | 380 | # define SYMBOL_VAL(sym) lisp_h_SYMBOL_VAL (sym) |
| 379 | # define SYMBOLP(x) lisp_h_SYMBOLP (x) | 381 | # define SYMBOLP(x) lisp_h_SYMBOLP (x) |
| 380 | # define VECTORLIKEP(x) lisp_h_VECTORLIKEP (x) | 382 | # define VECTORLIKEP(x) lisp_h_VECTORLIKEP (x) |
| @@ -600,6 +602,9 @@ extern void char_table_set (Lisp_Object, int, Lisp_Object); | |||
| 600 | /* Defined in data.c. */ | 602 | /* Defined in data.c. */ |
| 601 | extern _Noreturn Lisp_Object wrong_type_argument (Lisp_Object, Lisp_Object); | 603 | extern _Noreturn Lisp_Object wrong_type_argument (Lisp_Object, Lisp_Object); |
| 602 | extern _Noreturn void wrong_choice (Lisp_Object, Lisp_Object); | 604 | extern _Noreturn void wrong_choice (Lisp_Object, Lisp_Object); |
| 605 | extern void notify_variable_watchers (Lisp_Object symbol, Lisp_Object newval, | ||
| 606 | Lisp_Object operation, Lisp_Object where); | ||
| 607 | |||
| 603 | 608 | ||
| 604 | #ifdef CANNOT_DUMP | 609 | #ifdef CANNOT_DUMP |
| 605 | enum { might_dump = false }; | 610 | enum { might_dump = false }; |
| @@ -632,6 +637,13 @@ enum symbol_redirect | |||
| 632 | SYMBOL_FORWARDED = 3 | 637 | SYMBOL_FORWARDED = 3 |
| 633 | }; | 638 | }; |
| 634 | 639 | ||
| 640 | enum symbol_trapped_write | ||
| 641 | { | ||
| 642 | SYMBOL_UNTRAPPED_WRITE = 0, | ||
| 643 | SYMBOL_NOWRITE = 1, | ||
| 644 | SYMBOL_TRAPPED_WRITE = 2 | ||
| 645 | }; | ||
| 646 | |||
| 635 | struct Lisp_Symbol | 647 | struct Lisp_Symbol |
| 636 | { | 648 | { |
| 637 | bool_bf gcmarkbit : 1; | 649 | bool_bf gcmarkbit : 1; |
| @@ -643,10 +655,10 @@ struct Lisp_Symbol | |||
| 643 | 3 : it's a forwarding variable, the value is in `forward'. */ | 655 | 3 : it's a forwarding variable, the value is in `forward'. */ |
| 644 | ENUM_BF (symbol_redirect) redirect : 3; | 656 | ENUM_BF (symbol_redirect) redirect : 3; |
| 645 | 657 | ||
| 646 | /* Non-zero means symbol is constant, i.e. changing its value | 658 | /* 0 : normal case, just set the value |
| 647 | should signal an error. If the value is 3, then the var | 659 | 1 : constant, cannot set, e.g. nil, t, :keywords. |
| 648 | can be changed, but only by `defconst'. */ | 660 | 2 : trap the write, call watcher functions. */ |
| 649 | unsigned constant : 2; | 661 | ENUM_BF (symbol_trapped_write) trapped_write : 2; |
| 650 | 662 | ||
| 651 | /* Interned state of the symbol. This is an enumerator from | 663 | /* Interned state of the symbol. This is an enumerator from |
| 652 | enum symbol_interned. */ | 664 | enum symbol_interned. */ |
| @@ -1850,9 +1862,20 @@ SYMBOL_INTERNED_IN_INITIAL_OBARRAY_P (Lisp_Object sym) | |||
| 1850 | return XSYMBOL (sym)->interned == SYMBOL_INTERNED_IN_INITIAL_OBARRAY; | 1862 | return XSYMBOL (sym)->interned == SYMBOL_INTERNED_IN_INITIAL_OBARRAY; |
| 1851 | } | 1863 | } |
| 1852 | 1864 | ||
| 1853 | /* Value is non-zero if symbol is considered a constant, i.e. its | 1865 | /* Value is non-zero if symbol cannot be changed through a simple set, |
| 1854 | value cannot be changed (there is an exception for keyword symbols, | 1866 | i.e. it's a constant (e.g. nil, t, :keywords), or it has some |
| 1855 | whose value can be set to the keyword symbol itself). */ | 1867 | watching functions. */ |
| 1868 | |||
| 1869 | INLINE int | ||
| 1870 | (SYMBOL_TRAPPED_WRITE_P) (Lisp_Object sym) | ||
| 1871 | { | ||
| 1872 | return lisp_h_SYMBOL_TRAPPED_WRITE_P (sym); | ||
| 1873 | } | ||
| 1874 | |||
| 1875 | /* Value is non-zero if symbol cannot be changed at all, i.e. it's a | ||
| 1876 | constant (e.g. nil, t, :keywords). Code that actually wants to | ||
| 1877 | write to SYM, should also check whether there are any watching | ||
| 1878 | functions. */ | ||
| 1856 | 1879 | ||
| 1857 | INLINE int | 1880 | INLINE int |
| 1858 | (SYMBOL_CONSTANT_P) (Lisp_Object sym) | 1881 | (SYMBOL_CONSTANT_P) (Lisp_Object sym) |
| @@ -3286,6 +3309,12 @@ set_symbol_next (Lisp_Object sym, struct Lisp_Symbol *next) | |||
| 3286 | XSYMBOL (sym)->next = next; | 3309 | XSYMBOL (sym)->next = next; |
| 3287 | } | 3310 | } |
| 3288 | 3311 | ||
| 3312 | INLINE void | ||
| 3313 | make_symbol_constant (Lisp_Object sym) | ||
| 3314 | { | ||
| 3315 | XSYMBOL (sym)->trapped_write = SYMBOL_NOWRITE; | ||
| 3316 | } | ||
| 3317 | |||
| 3289 | /* Buffer-local (also frame-local) variable access functions. */ | 3318 | /* Buffer-local (also frame-local) variable access functions. */ |
| 3290 | 3319 | ||
| 3291 | INLINE int | 3320 | INLINE int |
| @@ -3394,7 +3423,13 @@ extern _Noreturn void args_out_of_range (Lisp_Object, Lisp_Object); | |||
| 3394 | extern _Noreturn void args_out_of_range_3 (Lisp_Object, Lisp_Object, | 3423 | extern _Noreturn void args_out_of_range_3 (Lisp_Object, Lisp_Object, |
| 3395 | Lisp_Object); | 3424 | Lisp_Object); |
| 3396 | extern Lisp_Object do_symval_forwarding (union Lisp_Fwd *); | 3425 | extern Lisp_Object do_symval_forwarding (union Lisp_Fwd *); |
| 3397 | extern void set_internal (Lisp_Object, Lisp_Object, Lisp_Object, bool); | 3426 | enum Set_Internal_Bind { |
| 3427 | SET_INTERNAL_SET, | ||
| 3428 | SET_INTERNAL_BIND, | ||
| 3429 | SET_INTERNAL_UNBIND | ||
| 3430 | }; | ||
| 3431 | extern void set_internal (Lisp_Object, Lisp_Object, Lisp_Object, | ||
| 3432 | enum Set_Internal_Bind); | ||
| 3398 | extern void syms_of_data (void); | 3433 | extern void syms_of_data (void); |
| 3399 | extern void swap_in_global_binding (struct Lisp_Symbol *); | 3434 | extern void swap_in_global_binding (struct Lisp_Symbol *); |
| 3400 | 3435 | ||
| @@ -3877,6 +3912,7 @@ extern _Noreturn void xsignal2 (Lisp_Object, Lisp_Object, Lisp_Object); | |||
| 3877 | extern _Noreturn void xsignal3 (Lisp_Object, Lisp_Object, Lisp_Object, | 3912 | extern _Noreturn void xsignal3 (Lisp_Object, Lisp_Object, Lisp_Object, |
| 3878 | Lisp_Object); | 3913 | Lisp_Object); |
| 3879 | extern _Noreturn void signal_error (const char *, Lisp_Object); | 3914 | extern _Noreturn void signal_error (const char *, Lisp_Object); |
| 3915 | extern Lisp_Object funcall_subr (struct Lisp_Subr *subr, ptrdiff_t numargs, Lisp_Object *arg_vector); | ||
| 3880 | extern Lisp_Object eval_sub (Lisp_Object form); | 3916 | extern Lisp_Object eval_sub (Lisp_Object form); |
| 3881 | extern Lisp_Object apply1 (Lisp_Object, Lisp_Object); | 3917 | extern Lisp_Object apply1 (Lisp_Object, Lisp_Object); |
| 3882 | extern Lisp_Object call0 (Lisp_Object); | 3918 | extern Lisp_Object call0 (Lisp_Object); |
diff --git a/src/lread.c b/src/lread.c index eab9b8bea08..14f9393cc47 100644 --- a/src/lread.c +++ b/src/lread.c | |||
| @@ -3833,7 +3833,7 @@ intern_sym (Lisp_Object sym, Lisp_Object obarray, Lisp_Object index) | |||
| 3833 | 3833 | ||
| 3834 | if (SREF (SYMBOL_NAME (sym), 0) == ':' && EQ (obarray, initial_obarray)) | 3834 | if (SREF (SYMBOL_NAME (sym), 0) == ':' && EQ (obarray, initial_obarray)) |
| 3835 | { | 3835 | { |
| 3836 | XSYMBOL (sym)->constant = 1; | 3836 | make_symbol_constant (sym); |
| 3837 | XSYMBOL (sym)->redirect = SYMBOL_PLAINVAL; | 3837 | XSYMBOL (sym)->redirect = SYMBOL_PLAINVAL; |
| 3838 | SET_SYMBOL_VAL (XSYMBOL (sym), sym); | 3838 | SET_SYMBOL_VAL (XSYMBOL (sym), sym); |
| 3839 | } | 3839 | } |
| @@ -4120,12 +4120,12 @@ init_obarray (void) | |||
| 4120 | 4120 | ||
| 4121 | DEFSYM (Qnil, "nil"); | 4121 | DEFSYM (Qnil, "nil"); |
| 4122 | SET_SYMBOL_VAL (XSYMBOL (Qnil), Qnil); | 4122 | SET_SYMBOL_VAL (XSYMBOL (Qnil), Qnil); |
| 4123 | XSYMBOL (Qnil)->constant = 1; | 4123 | make_symbol_constant (Qnil); |
| 4124 | XSYMBOL (Qnil)->declared_special = true; | 4124 | XSYMBOL (Qnil)->declared_special = true; |
| 4125 | 4125 | ||
| 4126 | DEFSYM (Qt, "t"); | 4126 | DEFSYM (Qt, "t"); |
| 4127 | SET_SYMBOL_VAL (XSYMBOL (Qt), Qt); | 4127 | SET_SYMBOL_VAL (XSYMBOL (Qt), Qt); |
| 4128 | XSYMBOL (Qt)->constant = 1; | 4128 | make_symbol_constant (Qt); |
| 4129 | XSYMBOL (Qt)->declared_special = true; | 4129 | XSYMBOL (Qt)->declared_special = true; |
| 4130 | 4130 | ||
| 4131 | /* Qt is correct even if CANNOT_DUMP. loadup.el will set to nil at end. */ | 4131 | /* Qt is correct even if CANNOT_DUMP. loadup.el will set to nil at end. */ |