diff options
| author | Stefan Monnier | 2012-11-12 22:00:09 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2012-11-12 22:00:09 -0500 |
| commit | 413d4689c0c3f616856615ac7b8bb047c5f2febd (patch) | |
| tree | 3f2ca64880fb237665d78b4d19d1fe4ab400fb6a | |
| parent | f78ee6afc094cdfd6162bfd645836e84875dcddf (diff) | |
| download | emacs-413d4689c0c3f616856615ac7b8bb047c5f2febd.tar.gz emacs-413d4689c0c3f616856615ac7b8bb047c5f2febd.zip | |
* lisp/emacs-lisp/cl.el (dolist, dotimes, declare): Use advice-add to
override the default.
* lisp/emacs-lisp/cl-macs.el (cl-dolist, cl-dotimes): Rewrite without using
cl--dotimes/dolist.
* lisp/subr.el (dolist, dotimes, declare): Redefine them normally, even when
`cl' is loaded.
* lisp/emacs-lisp/nadvice.el (advice--normalize): New function, extracted
from add-advice.
(advice--strip-macro): New function.
(advice--defalias-fset): Use them to handle macros.
(advice-add): Use them.
(advice-member-p): Correctly handle macros.
| -rw-r--r-- | lisp/ChangeLog | 16 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-loaddefs.el | 2 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-macs.el | 12 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl.el | 20 | ||||
| -rw-r--r-- | lisp/emacs-lisp/nadvice.el | 87 | ||||
| -rw-r--r-- | lisp/subr.el | 6 | ||||
| -rw-r--r-- | test/automated/advice-tests.el | 7 |
7 files changed, 91 insertions, 59 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 6ab2880f09f..92f3343db64 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,5 +1,21 @@ | |||
| 1 | 2012-11-13 Stefan Monnier <monnier@iro.umontreal.ca> | 1 | 2012-11-13 Stefan Monnier <monnier@iro.umontreal.ca> |
| 2 | 2 | ||
| 3 | * emacs-lisp/cl.el (dolist, dotimes, declare): Use advice-add to | ||
| 4 | override the default. | ||
| 5 | * emacs-lisp/cl-macs.el (cl-dolist, cl-dotimes): Rewrite without using | ||
| 6 | cl--dotimes/dolist. | ||
| 7 | * subr.el (dolist, dotimes, declare): Redefine them normally, even when | ||
| 8 | `cl' is loaded. | ||
| 9 | |||
| 10 | * emacs-lisp/nadvice.el (advice--normalize): New function, extracted | ||
| 11 | from add-advice. | ||
| 12 | (advice--strip-macro): New function. | ||
| 13 | (advice--defalias-fset): Use them to handle macros. | ||
| 14 | (advice-add): Use them. | ||
| 15 | (advice-member-p): Correctly handle macros. | ||
| 16 | |||
| 17 | 2012-11-13 Stefan Monnier <monnier@iro.umontreal.ca> | ||
| 18 | |||
| 3 | * emacs-lisp/gv.el (gv-define-simple-setter): One more fix (bug#12871). | 19 | * emacs-lisp/gv.el (gv-define-simple-setter): One more fix (bug#12871). |
| 4 | 20 | ||
| 5 | 2012-11-13 Wolfgang Jenkner <wjenkner@inode.at> | 21 | 2012-11-13 Wolfgang Jenkner <wjenkner@inode.at> |
diff --git a/lisp/emacs-lisp/cl-loaddefs.el b/lisp/emacs-lisp/cl-loaddefs.el index bf99af2f7e6..eb58d17c02e 100644 --- a/lisp/emacs-lisp/cl-loaddefs.el +++ b/lisp/emacs-lisp/cl-loaddefs.el | |||
| @@ -267,7 +267,7 @@ including `cl-block' and `cl-eval-when'. | |||
| 267 | ;;;;;; cl-typecase cl-ecase cl-case cl-load-time-value cl-eval-when | 267 | ;;;;;; cl-typecase cl-ecase cl-case cl-load-time-value cl-eval-when |
| 268 | ;;;;;; cl-destructuring-bind cl-function cl-defmacro cl-defun cl-gentemp | 268 | ;;;;;; cl-destructuring-bind cl-function cl-defmacro cl-defun cl-gentemp |
| 269 | ;;;;;; cl-gensym cl--compiler-macro-cXXr cl--compiler-macro-list*) | 269 | ;;;;;; cl-gensym cl--compiler-macro-cXXr cl--compiler-macro-list*) |
| 270 | ;;;;;; "cl-macs" "cl-macs.el" "a7228877484d2b39e1c2bee40b011734") | 270 | ;;;;;; "cl-macs" "cl-macs.el" "c7ad09a74a1d2969406e7e2aaf3812fc") |
| 271 | ;;; Generated autoloads from cl-macs.el | 271 | ;;; Generated autoloads from cl-macs.el |
| 272 | 272 | ||
| 273 | (autoload 'cl--compiler-macro-list* "cl-macs" "\ | 273 | (autoload 'cl--compiler-macro-list* "cl-macs" "\ |
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index b28f8f7f9e9..3c46c40242d 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el | |||
| @@ -1547,9 +1547,9 @@ An implicit nil block is established around the loop. | |||
| 1547 | \(fn (VAR LIST [RESULT]) BODY...)" | 1547 | \(fn (VAR LIST [RESULT]) BODY...)" |
| 1548 | (declare (debug ((symbolp form &optional form) cl-declarations body)) | 1548 | (declare (debug ((symbolp form &optional form) cl-declarations body)) |
| 1549 | (indent 1)) | 1549 | (indent 1)) |
| 1550 | `(cl-block nil | 1550 | (let ((loop `(dolist ,spec ,@body))) |
| 1551 | (,(if (eq 'cl-dolist (symbol-function 'dolist)) 'cl--dolist 'dolist) | 1551 | (if (advice-member-p #'cl--wrap-in-nil-block 'dolist) |
| 1552 | ,spec ,@body))) | 1552 | loop `(cl-block nil ,loop)))) |
| 1553 | 1553 | ||
| 1554 | ;;;###autoload | 1554 | ;;;###autoload |
| 1555 | (defmacro cl-dotimes (spec &rest body) | 1555 | (defmacro cl-dotimes (spec &rest body) |
| @@ -1560,9 +1560,9 @@ nil. | |||
| 1560 | 1560 | ||
| 1561 | \(fn (VAR COUNT [RESULT]) BODY...)" | 1561 | \(fn (VAR COUNT [RESULT]) BODY...)" |
| 1562 | (declare (debug cl-dolist) (indent 1)) | 1562 | (declare (debug cl-dolist) (indent 1)) |
| 1563 | `(cl-block nil | 1563 | (let ((loop `(dotimes ,spec ,@body))) |
| 1564 | (,(if (eq 'cl-dotimes (symbol-function 'dotimes)) 'cl--dotimes 'dotimes) | 1564 | (if (advice-member-p #'cl--wrap-in-nil-block 'dotimes) |
| 1565 | ,spec ,@body))) | 1565 | loop `(cl-block nil ,loop)))) |
| 1566 | 1566 | ||
| 1567 | ;;;###autoload | 1567 | ;;;###autoload |
| 1568 | (defmacro cl-do-symbols (spec &rest body) | 1568 | (defmacro cl-do-symbols (spec &rest body) |
diff --git a/lisp/emacs-lisp/cl.el b/lisp/emacs-lisp/cl.el index 016967bc713..40d12358b17 100644 --- a/lisp/emacs-lisp/cl.el +++ b/lisp/emacs-lisp/cl.el | |||
| @@ -107,14 +107,6 @@ | |||
| 107 | )) | 107 | )) |
| 108 | (defvaralias var (intern (format "cl-%s" var)))) | 108 | (defvaralias var (intern (format "cl-%s" var)))) |
| 109 | 109 | ||
| 110 | ;; Before overwriting subr.el's `dotimes' and `dolist', let's remember | ||
| 111 | ;; them under a different name, so we can use them in our implementation | ||
| 112 | ;; of `dotimes' and `dolist'. | ||
| 113 | (unless (fboundp 'cl--dotimes) | ||
| 114 | (defalias 'cl--dotimes (symbol-function 'dotimes) "The non-CL `dotimes'.")) | ||
| 115 | (unless (fboundp 'cl--dolist) | ||
| 116 | (defalias 'cl--dolist (symbol-function 'dolist) "The non-CL `dolist'.")) | ||
| 117 | |||
| 118 | (dolist (fun '( | 110 | (dolist (fun '( |
| 119 | (get* . cl-get) | 111 | (get* . cl-get) |
| 120 | (random* . cl-random) | 112 | (random* . cl-random) |
| @@ -228,7 +220,6 @@ | |||
| 228 | remf | 220 | remf |
| 229 | psetf | 221 | psetf |
| 230 | (define-setf-method . define-setf-expander) | 222 | (define-setf-method . define-setf-expander) |
| 231 | declare | ||
| 232 | the | 223 | the |
| 233 | locally | 224 | locally |
| 234 | multiple-value-setq | 225 | multiple-value-setq |
| @@ -239,8 +230,6 @@ | |||
| 239 | psetq | 230 | psetq |
| 240 | do-all-symbols | 231 | do-all-symbols |
| 241 | do-symbols | 232 | do-symbols |
| 242 | dotimes | ||
| 243 | dolist | ||
| 244 | do* | 233 | do* |
| 245 | do | 234 | do |
| 246 | loop | 235 | loop |
| @@ -322,6 +311,15 @@ | |||
| 322 | (intern (format "cl-%s" fun))))) | 311 | (intern (format "cl-%s" fun))))) |
| 323 | (defalias fun new))) | 312 | (defalias fun new))) |
| 324 | 313 | ||
| 314 | (defun cl--wrap-in-nil-block (fun &rest args) | ||
| 315 | `(cl-block nil ,(apply fun args))) | ||
| 316 | (advice-add 'dolist :around #'cl--wrap-in-nil-block) | ||
| 317 | (advice-add 'dotimes :around #'cl--wrap-in-nil-block) | ||
| 318 | |||
| 319 | (defun cl--pass-args-to-cl-declare (&rest specs) | ||
| 320 | (macroexpand `(cl-declare ,@specs))) | ||
| 321 | (advice-add 'declare :after #'cl--pass-args-to-cl-declare) | ||
| 322 | |||
| 325 | ;;; Features provided a bit differently in Elisp. | 323 | ;;; Features provided a bit differently in Elisp. |
| 326 | 324 | ||
| 327 | ;; First, the old lexical-let is now better served by `lexical-binding', tho | 325 | ;; First, the old lexical-let is now better served by `lexical-binding', tho |
diff --git a/lisp/emacs-lisp/nadvice.el b/lisp/emacs-lisp/nadvice.el index 020a2f89bdb..ca1ebf3cad2 100644 --- a/lisp/emacs-lisp/nadvice.el +++ b/lisp/emacs-lisp/nadvice.el | |||
| @@ -230,23 +230,49 @@ of the piece of advice." | |||
| 230 | (advice--make-1 (aref old 1) (aref old 3) | 230 | (advice--make-1 (aref old 1) (aref old 3) |
| 231 | first nrest props))))) | 231 | first nrest props))))) |
| 232 | 232 | ||
| 233 | (defun advice--normalize (symbol def) | ||
| 234 | (cond | ||
| 235 | ((special-form-p def) | ||
| 236 | ;; Not worth the trouble trying to handle this, I think. | ||
| 237 | (error "add-advice failure: %S is a special form" symbol)) | ||
| 238 | ((and (symbolp def) | ||
| 239 | (eq 'macro (car-safe (ignore-errors (indirect-function def))))) | ||
| 240 | (let ((newval (cons 'macro (cdr (indirect-function def))))) | ||
| 241 | (put symbol 'advice--saved-rewrite (cons def newval)) | ||
| 242 | newval)) | ||
| 243 | ;; `f' might be a pure (hence read-only) cons! | ||
| 244 | ((and (eq 'macro (car-safe def)) | ||
| 245 | (not (ignore-errors (setcdr def (cdr def)) t))) | ||
| 246 | (cons 'macro (cdr def))) | ||
| 247 | (t def))) | ||
| 248 | |||
| 249 | (defsubst advice--strip-macro (x) | ||
| 250 | (if (eq 'macro (car-safe x)) (cdr x) x)) | ||
| 251 | |||
| 233 | (defun advice--defalias-fset (fsetfun symbol newdef) | 252 | (defun advice--defalias-fset (fsetfun symbol newdef) |
| 234 | (let* ((olddef (if (fboundp symbol) (symbol-function symbol))) | 253 | (when (get symbol 'advice--saved-rewrite) |
| 254 | (put symbol 'advice--saved-rewrite nil)) | ||
| 255 | (setq newdef (advice--normalize symbol newdef)) | ||
| 256 | (let* ((olddef (advice--strip-macro | ||
| 257 | (if (fboundp symbol) (symbol-function symbol)))) | ||
| 235 | (oldadv | 258 | (oldadv |
| 236 | (cond | 259 | (cond |
| 237 | ((null (get symbol 'advice--pending)) | 260 | ((null (get symbol 'advice--pending)) |
| 238 | (or olddef | 261 | (or olddef |
| 239 | (progn | 262 | (progn |
| 240 | (message "Delayed advice activation failed for %s: no data" | 263 | (message "Delayed advice activation failed for %s: no data" |
| 241 | symbol) | 264 | symbol) |
| 242 | nil))) | 265 | nil))) |
| 243 | ((or (not olddef) (autoloadp olddef)) | 266 | ((or (not olddef) (autoloadp olddef)) |
| 244 | (prog1 (get symbol 'advice--pending) | 267 | (prog1 (get symbol 'advice--pending) |
| 245 | (put symbol 'advice--pending nil))) | 268 | (put symbol 'advice--pending nil))) |
| 246 | (t (message "Dropping left-over advice--pending for %s" symbol) | 269 | (t (message "Dropping left-over advice--pending for %s" symbol) |
| 247 | (put symbol 'advice--pending nil) | 270 | (put symbol 'advice--pending nil) |
| 248 | olddef)))) | 271 | olddef)))) |
| 249 | (funcall (or fsetfun #'fset) symbol (advice--subst-main oldadv newdef)))) | 272 | (let* ((snewdef (advice--strip-macro newdef)) |
| 273 | (snewadv (advice--subst-main oldadv snewdef))) | ||
| 274 | (funcall (or fsetfun #'fset) symbol | ||
| 275 | (if (eq snewdef newdef) snewadv (cons 'macro snewadv)))))) | ||
| 250 | 276 | ||
| 251 | 277 | ||
| 252 | ;;;###autoload | 278 | ;;;###autoload |
| @@ -269,29 +295,18 @@ is defined as a macro, alias, command, ..." | |||
| 269 | ;; simplest way is to make advice.el build one ad-Advice-foo function for | 295 | ;; simplest way is to make advice.el build one ad-Advice-foo function for |
| 270 | ;; each advised function which is advice-added/removed whenever ad-activate | 296 | ;; each advised function which is advice-added/removed whenever ad-activate |
| 271 | ;; ad-deactivate is called. | 297 | ;; ad-deactivate is called. |
| 272 | (let ((f (and (fboundp symbol) (symbol-function symbol)))) | 298 | (let* ((f (and (fboundp symbol) (symbol-function symbol))) |
| 273 | (cond | 299 | (nf (advice--normalize symbol f))) |
| 274 | ((special-form-p f) | 300 | (unless (eq f nf) ;; Most importantly, if nf == nil! |
| 275 | ;; Not worth the trouble trying to handle this, I think. | 301 | (fset symbol nf)) |
| 276 | (error "add-advice failure: %S is a special form" symbol)) | ||
| 277 | ((and (symbolp f) | ||
| 278 | (eq 'macro (car-safe (ignore-errors (indirect-function f))))) | ||
| 279 | (let ((newval (cons 'macro (cdr (indirect-function f))))) | ||
| 280 | (put symbol 'advice--saved-rewrite (cons f newval)) | ||
| 281 | (fset symbol newval))) | ||
| 282 | ;; `f' might be a pure (hence read-only) cons! | ||
| 283 | ((and (eq 'macro (car-safe f)) (not (ignore-errors (setcdr f (cdr f)) t))) | ||
| 284 | (fset symbol (cons 'macro (cdr f)))) | ||
| 285 | )) | ||
| 286 | (let ((f (and (fboundp symbol) (symbol-function symbol)))) | ||
| 287 | (add-function where (cond | 302 | (add-function where (cond |
| 288 | ((eq (car-safe f) 'macro) (cdr f)) | 303 | ((eq (car-safe nf) 'macro) (cdr nf)) |
| 289 | ;; If the function is not yet defined, we can't yet | 304 | ;; If the function is not yet defined, we can't yet |
| 290 | ;; install the advice. | 305 | ;; install the advice. |
| 291 | ;; FIXME: If it's an autoloaded command, we also | 306 | ;; FIXME: If it's an autoloaded command, we also |
| 292 | ;; have a problem because we need to load the | 307 | ;; have a problem because we need to load the |
| 293 | ;; command to build the interactive-form. | 308 | ;; command to build the interactive-form. |
| 294 | ((or (not f) (and (autoloadp f))) ;; (commandp f) | 309 | ((or (not nf) (and (autoloadp nf))) ;; (commandp nf) |
| 295 | (get symbol 'advice--pending)) | 310 | (get symbol 'advice--pending)) |
| 296 | (t (symbol-function symbol))) | 311 | (t (symbol-function symbol))) |
| 297 | function props) | 312 | function props) |
| @@ -316,7 +331,7 @@ of the piece of advice." | |||
| 316 | function) | 331 | function) |
| 317 | (unless (advice--p | 332 | (unless (advice--p |
| 318 | (if (eq (car-safe f) 'macro) (cdr f) (symbol-function symbol))) | 333 | (if (eq (car-safe f) 'macro) (cdr f) (symbol-function symbol))) |
| 319 | ;; Not adviced any more. | 334 | ;; Not advised any more. |
| 320 | (remove-function (get symbol 'defalias-fset-function) | 335 | (remove-function (get symbol 'defalias-fset-function) |
| 321 | #'advice--defalias-fset) | 336 | #'advice--defalias-fset) |
| 322 | (if (eq (symbol-function symbol) | 337 | (if (eq (symbol-function symbol) |
| @@ -335,13 +350,15 @@ of the piece of advice." | |||
| 335 | ;; (setq def (advice--cdr def))))) | 350 | ;; (setq def (advice--cdr def))))) |
| 336 | 351 | ||
| 337 | ;;;###autoload | 352 | ;;;###autoload |
| 338 | (defun advice-member-p (function symbol) | 353 | (defun advice-member-p (advice function-name) |
| 339 | "Return non-nil if advice FUNCTION has been added to function SYMBOL. | 354 | "Return non-nil if ADVICE has been added to FUNCTION-NAME. |
| 340 | Instead of FUNCTION being the actual function, it can also be the `name' | 355 | Instead of ADVICE being the actual function, it can also be the `name' |
| 341 | of the piece of advice." | 356 | of the piece of advice." |
| 342 | (advice--member-p function | 357 | (advice--member-p advice |
| 343 | (or (get symbol 'advice--pending) | 358 | (or (get function-name 'advice--pending) |
| 344 | (if (fboundp symbol) (symbol-function symbol))))) | 359 | (advice--strip-macro |
| 360 | (if (fboundp function-name) | ||
| 361 | (symbol-function function-name)))))) | ||
| 345 | 362 | ||
| 346 | 363 | ||
| 347 | (provide 'nadvice) | 364 | (provide 'nadvice) |
diff --git a/lisp/subr.el b/lisp/subr.el index ebfcfbc0930..b0ac2dd2106 100644 --- a/lisp/subr.el +++ b/lisp/subr.el | |||
| @@ -195,11 +195,6 @@ value of last one, or nil if there are none. | |||
| 195 | (declare (indent 1) (debug t)) | 195 | (declare (indent 1) (debug t)) |
| 196 | (cons 'if (cons cond (cons nil body)))) | 196 | (cons 'if (cons cond (cons nil body)))) |
| 197 | 197 | ||
| 198 | (if (null (featurep 'cl)) | ||
| 199 | (progn | ||
| 200 | ;; If we reload subr.el after having loaded CL, be careful not to | ||
| 201 | ;; overwrite CL's extended definition of `dolist', `dotimes', `declare'. | ||
| 202 | |||
| 203 | (defmacro dolist (spec &rest body) | 198 | (defmacro dolist (spec &rest body) |
| 204 | "Loop over a list. | 199 | "Loop over a list. |
| 205 | Evaluate BODY with VAR bound to each car from LIST, in turn. | 200 | Evaluate BODY with VAR bound to each car from LIST, in turn. |
| @@ -279,7 +274,6 @@ The possible values of SPECS are specified by | |||
| 279 | `defun-declarations-alist' and `macro-declarations-alist'." | 274 | `defun-declarations-alist' and `macro-declarations-alist'." |
| 280 | ;; FIXME: edebug spec should pay attention to defun-declarations-alist. | 275 | ;; FIXME: edebug spec should pay attention to defun-declarations-alist. |
| 281 | nil) | 276 | nil) |
| 282 | )) | ||
| 283 | 277 | ||
| 284 | (defmacro ignore-errors (&rest body) | 278 | (defmacro ignore-errors (&rest body) |
| 285 | "Execute BODY; if an error occurs, return nil. | 279 | "Execute BODY; if an error occurs, return nil. |
diff --git a/test/automated/advice-tests.el b/test/automated/advice-tests.el index cac10e9602f..9f9719fdcfc 100644 --- a/test/automated/advice-tests.el +++ b/test/automated/advice-tests.el | |||
| @@ -50,6 +50,13 @@ | |||
| 50 | ((ad-activate 'sm-test2) | 50 | ((ad-activate 'sm-test2) |
| 51 | (sm-test2 6) 20) | 51 | (sm-test2 6) 20) |
| 52 | ((null (get 'sm-test2 'defalias-fset-function)) t) | 52 | ((null (get 'sm-test2 'defalias-fset-function)) t) |
| 53 | |||
| 54 | ((advice-add 'sm-test3 :around | ||
| 55 | (lambda (f &rest args) `(toto ,(apply f args))) | ||
| 56 | '((name . wrap-with-toto))) | ||
| 57 | (defmacro sm-test3 (x) `(call-test3 ,x)) | ||
| 58 | (macroexpand '(sm-test3 56)) (toto (call-test3 56))) | ||
| 59 | |||
| 53 | )) | 60 | )) |
| 54 | 61 | ||
| 55 | (ert-deftest advice-tests () | 62 | (ert-deftest advice-tests () |