aboutsummaryrefslogtreecommitdiffstats
path: root/src/data.c
diff options
context:
space:
mode:
authorNoam Postavsky2015-11-19 19:50:06 -0500
committerNoam Postavsky2016-12-02 20:25:14 -0500
commit227213164e06363f0a4fb2beeeb647c99749299e (patch)
tree8fda48112af0631ce9b6c595e33101a9b5961909 /src/data.c
parent0fc4761ca88175c30da7209c9ab1cde788b66a76 (diff)
downloademacs-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.c191
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
1240void 1241void
1241set_internal (Lisp_Object symbol, Lisp_Object newval, Lisp_Object where, 1242set_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
1403static void
1404set_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
1415static void
1416restore_symbol_trapped_write (Lisp_Object symbol)
1417{
1418 set_symbol_trapped_write (symbol, SYMBOL_TRAPPED_WRITE);
1419}
1420
1421static void
1422harmonize_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
1430DEFUN ("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.
1433All 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
1447DEFUN ("remove-variable-watcher", Fremove_variable_watcher, Sremove_variable_watcher,
1448 2, 2, 0,
1449 doc: /* Undo the effect of `add-variable-watcher'.
1450Remove WATCH-FUNCTION from the list of functions to be called when
1451SYMBOL (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
1466void
1467notify_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}