diff options
| author | Stefan Monnier | 2011-04-13 14:56:47 -0300 |
|---|---|---|
| committer | Stefan Monnier | 2011-04-13 14:56:47 -0300 |
| commit | c2bd2ab02856f36d41c88f5e054f4444a6366d5e (patch) | |
| tree | f4b359472e89a3b2012f88f2c860a295652e8bcf | |
| parent | c0ece6a5c4c8dc87be1da6808289c88de19d8398 (diff) | |
| download | emacs-c2bd2ab02856f36d41c88f5e054f4444a6366d5e.tar.gz emacs-c2bd2ab02856f36d41c88f5e054f4444a6366d5e.zip | |
Preserve arg names for advice of subr and lexical functions.
* lisp/help-fns.el (help-function-arglist): Consolidate the subr and
new-byte-code cases. Add argument `preserve-names' to extract names
from the docstring when needed.
* lisp/emacs-lisp/advice.el (ad-define-subr-args, ad-undefine-subr-args)
(ad-subr-args-defined-p, ad-get-subr-args, ad-subr-arglist): Remove.
(ad-arglist): Use help-function-arglist's new arg.
(ad-definition-type): Use cond.
Fixes: debbugs:8457
| -rw-r--r-- | lisp/ChangeLog | 11 | ||||
| -rw-r--r-- | lisp/emacs-lisp/advice.el | 141 | ||||
| -rw-r--r-- | lisp/help-fns.el | 75 |
3 files changed, 68 insertions, 159 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index e18f562eaf3..cb3aebb2682 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,14 @@ | |||
| 1 | 2011-04-13 Stefan Monnier <monnier@iro.umontreal.ca> | ||
| 2 | |||
| 3 | Preserve arg names for advice of subr and lexical functions (bug#8457). | ||
| 4 | * help-fns.el (help-function-arglist): Consolidate the subr and | ||
| 5 | new-byte-code cases. Add argument `preserve-names' to extract names | ||
| 6 | from the docstring when needed. | ||
| 7 | * emacs-lisp/advice.el (ad-define-subr-args, ad-undefine-subr-args) | ||
| 8 | (ad-subr-args-defined-p, ad-get-subr-args, ad-subr-arglist): Remove. | ||
| 9 | (ad-arglist): Use help-function-arglist's new arg. | ||
| 10 | (ad-definition-type): Use cond. | ||
| 11 | |||
| 1 | 2011-04-13 Juanma Barranquero <lekktu@gmail.com> | 12 | 2011-04-13 Juanma Barranquero <lekktu@gmail.com> |
| 2 | 13 | ||
| 3 | * autorevert.el (auto-revert-handler): | 14 | * autorevert.el (auto-revert-handler): |
diff --git a/lisp/emacs-lisp/advice.el b/lisp/emacs-lisp/advice.el index 39ea97aa98e..5934975e36a 100644 --- a/lisp/emacs-lisp/advice.el +++ b/lisp/emacs-lisp/advice.el | |||
| @@ -503,36 +503,6 @@ | |||
| 503 | ;; exact structure of the original argument list as long as the new argument | 503 | ;; exact structure of the original argument list as long as the new argument |
| 504 | ;; list takes a compatible number/magnitude of actual arguments. | 504 | ;; list takes a compatible number/magnitude of actual arguments. |
| 505 | 505 | ||
| 506 | ;; @@@ Definition of subr argument lists: | ||
| 507 | ;; ====================================== | ||
| 508 | ;; When advice constructs the advised definition of a function it has to | ||
| 509 | ;; know the argument list of the original function. For functions and macros | ||
| 510 | ;; the argument list can be determined from the actual definition, however, | ||
| 511 | ;; for subrs there is no such direct access available. In Lemacs and for some | ||
| 512 | ;; subrs in Emacs-19 the argument list of a subr can be determined from | ||
| 513 | ;; its documentation string, in a v18 Emacs even that is not possible. If | ||
| 514 | ;; advice cannot at all determine the argument list of a subr it uses | ||
| 515 | ;; `(&rest ad-subr-args)' which will always work but is inefficient because | ||
| 516 | ;; it conses up arguments. The macro `ad-define-subr-args' can be used by | ||
| 517 | ;; the advice programmer to explicitly tell advice about the argument list | ||
| 518 | ;; of a certain subr, for example, | ||
| 519 | ;; | ||
| 520 | ;; (ad-define-subr-args 'fset '(sym newdef)) | ||
| 521 | ;; | ||
| 522 | ;; is used by advice itself to tell a v18 Emacs about the arguments of `fset'. | ||
| 523 | ;; The following can be used to undo such a definition: | ||
| 524 | ;; | ||
| 525 | ;; (ad-undefine-subr-args 'fset) | ||
| 526 | ;; | ||
| 527 | ;; The argument list definition is stored on the property list of the subr | ||
| 528 | ;; name symbol. When an argument list could be determined from the | ||
| 529 | ;; documentation string it will be cached under that property. The general | ||
| 530 | ;; mechanism for looking up the argument list of a subr is the following: | ||
| 531 | ;; 1) look for a definition stored on the property list | ||
| 532 | ;; 2) if that failed try to infer it from the documentation string and | ||
| 533 | ;; if successful cache it on the property list | ||
| 534 | ;; 3) otherwise use `(&rest ad-subr-args)' | ||
| 535 | |||
| 536 | ;; @@ Activation and deactivation: | 506 | ;; @@ Activation and deactivation: |
| 537 | ;; =============================== | 507 | ;; =============================== |
| 538 | ;; The definition of an advised function does not change until all its advice | 508 | ;; The definition of an advised function does not change until all its advice |
| @@ -1654,41 +1624,6 @@ | |||
| 1654 | ;; (fii 3 2) | 1624 | ;; (fii 3 2) |
| 1655 | ;; 5 | 1625 | ;; 5 |
| 1656 | ;; | 1626 | ;; |
| 1657 | ;; @@ Specifying argument lists of subrs: | ||
| 1658 | ;; ====================================== | ||
| 1659 | ;; The argument lists of subrs cannot be determined directly from Lisp. | ||
| 1660 | ;; This means that Advice has to use `(&rest ad-subr-args)' as the | ||
| 1661 | ;; argument list of the advised subr which is not very efficient. In Lemacs | ||
| 1662 | ;; subr argument lists can be determined from their documentation string, in | ||
| 1663 | ;; Emacs-19 this is the case for some but not all subrs. To accommodate | ||
| 1664 | ;; for the cases where the argument lists cannot be determined (e.g., in a | ||
| 1665 | ;; v18 Emacs) Advice comes with a specification mechanism that allows the | ||
| 1666 | ;; advice programmer to tell advice what the argument list of a certain subr | ||
| 1667 | ;; really is. | ||
| 1668 | ;; | ||
| 1669 | ;; In a v18 Emacs the following will return the &rest idiom: | ||
| 1670 | ;; | ||
| 1671 | ;; (ad-arglist (symbol-function 'car)) | ||
| 1672 | ;; (&rest ad-subr-args) | ||
| 1673 | ;; | ||
| 1674 | ;; To tell advice what the argument list of `car' really is we | ||
| 1675 | ;; can do the following: | ||
| 1676 | ;; | ||
| 1677 | ;; (ad-define-subr-args 'car '(list)) | ||
| 1678 | ;; ((list)) | ||
| 1679 | ;; | ||
| 1680 | ;; Now `ad-arglist' will return the proper argument list (this method is | ||
| 1681 | ;; actually used by advice itself for the advised definition of `fset'): | ||
| 1682 | ;; | ||
| 1683 | ;; (ad-arglist (symbol-function 'car)) | ||
| 1684 | ;; (list) | ||
| 1685 | ;; | ||
| 1686 | ;; The defined argument list will be stored on the property list of the | ||
| 1687 | ;; subr name symbol. When advice looks for a subr argument list it first | ||
| 1688 | ;; checks for a definition on the property list, if that fails it tries | ||
| 1689 | ;; to infer it from the documentation string and caches it on the property | ||
| 1690 | ;; list if it was successful, otherwise `(&rest ad-subr-args)' will be used. | ||
| 1691 | ;; | ||
| 1692 | ;; @@ Advising interactive subrs: | 1627 | ;; @@ Advising interactive subrs: |
| 1693 | ;; ============================== | 1628 | ;; ============================== |
| 1694 | ;; For the most part there is no difference between advising functions and | 1629 | ;; For the most part there is no difference between advising functions and |
| @@ -2536,52 +2471,11 @@ See Info node `(elisp)Computed Advice' for detailed documentation." | |||
| 2536 | If DEFINITION could be from a subr then its NAME should be | 2471 | If DEFINITION could be from a subr then its NAME should be |
| 2537 | supplied to make subr arglist lookup more efficient." | 2472 | supplied to make subr arglist lookup more efficient." |
| 2538 | (require 'help-fns) | 2473 | (require 'help-fns) |
| 2539 | (cond | 2474 | (help-function-arglist |
| 2540 | ((or (ad-macro-p definition) (ad-advice-p definition)) | 2475 | (if (or (ad-macro-p definition) (ad-advice-p definition)) |
| 2541 | (help-function-arglist (cdr definition))) | 2476 | (cdr definition) |
| 2542 | (t (help-function-arglist definition)))) | 2477 | definition) |
| 2543 | 2478 | 'preserve-names)) | |
| 2544 | ;; Store subr-args as `((arg1 arg2 ...))' so I can distinguish | ||
| 2545 | ;; a defined empty arglist `(nil)' from an undefined arglist: | ||
| 2546 | (defmacro ad-define-subr-args (subr arglist) | ||
| 2547 | `(put ,subr 'ad-subr-arglist (list ,arglist))) | ||
| 2548 | (defmacro ad-undefine-subr-args (subr) | ||
| 2549 | `(put ,subr 'ad-subr-arglist nil)) | ||
| 2550 | (defmacro ad-subr-args-defined-p (subr) | ||
| 2551 | `(get ,subr 'ad-subr-arglist)) | ||
| 2552 | (defmacro ad-get-subr-args (subr) | ||
| 2553 | `(car (get ,subr 'ad-subr-arglist))) | ||
| 2554 | |||
| 2555 | (defun ad-subr-arglist (subr-name) | ||
| 2556 | "Retrieve arglist of the subr with SUBR-NAME. | ||
| 2557 | Either use the one stored under the `ad-subr-arglist' property, | ||
| 2558 | or try to retrieve it from the docstring and cache it under | ||
| 2559 | that property, or otherwise use `(&rest ad-subr-args)'." | ||
| 2560 | (if (ad-subr-args-defined-p subr-name) | ||
| 2561 | (ad-get-subr-args subr-name) | ||
| 2562 | ;; says jwz: Should use this for Lemacs 19.8 and above: | ||
| 2563 | ;;((fboundp 'subr-min-args) | ||
| 2564 | ;; ...) | ||
| 2565 | ;; says hans: I guess what Jamie means is that I should use the values | ||
| 2566 | ;; of `subr-min-args' and `subr-max-args' to construct the subr arglist | ||
| 2567 | ;; without having to look it up via parsing the docstring, e.g., | ||
| 2568 | ;; values 1 and 2 would suggest `(arg1 &optional arg2)' as an | ||
| 2569 | ;; argument list. However, that won't work because there is no | ||
| 2570 | ;; way to distinguish a subr with args `(a &optional b &rest c)' from | ||
| 2571 | ;; one with args `(a &rest c)' using that mechanism. Also, the argument | ||
| 2572 | ;; names from the docstring are more meaningful. Hence, I'll stick with | ||
| 2573 | ;; the old way of doing things. | ||
| 2574 | (let ((doc (or (ad-real-documentation subr-name t) ""))) | ||
| 2575 | (if (not (string-match "\n\n\\((.+)\\)\\'" doc)) | ||
| 2576 | ;; Signalling an error leads to bugs during bootstrapping because | ||
| 2577 | ;; the DOC file is not yet built (which is an error, BTW). | ||
| 2578 | ;; (error "The usage info is missing from the subr %s" subr-name) | ||
| 2579 | '(&rest ad-subr-args) | ||
| 2580 | (ad-define-subr-args | ||
| 2581 | subr-name | ||
| 2582 | (cdr (car (read-from-string | ||
| 2583 | (downcase (match-string 1 doc)))))) | ||
| 2584 | (ad-get-subr-args subr-name))))) | ||
| 2585 | 2479 | ||
| 2586 | (defun ad-docstring (definition) | 2480 | (defun ad-docstring (definition) |
| 2587 | "Return the unexpanded docstring of DEFINITION." | 2481 | "Return the unexpanded docstring of DEFINITION." |
| @@ -2629,17 +2523,16 @@ definition (see the code for `documentation')." | |||
| 2629 | 2523 | ||
| 2630 | (defun ad-definition-type (definition) | 2524 | (defun ad-definition-type (definition) |
| 2631 | "Return symbol that describes the type of DEFINITION." | 2525 | "Return symbol that describes the type of DEFINITION." |
| 2632 | (if (ad-macro-p definition) | 2526 | (cond |
| 2633 | 'macro | 2527 | ((ad-macro-p definition) 'macro) |
| 2634 | (if (ad-subr-p definition) | 2528 | ((ad-subr-p definition) |
| 2635 | (if (ad-special-form-p definition) | 2529 | (if (ad-special-form-p definition) |
| 2636 | 'special-form | 2530 | 'special-form |
| 2637 | 'subr) | 2531 | 'subr)) |
| 2638 | (if (or (ad-lambda-p definition) | 2532 | ((or (ad-lambda-p definition) |
| 2639 | (ad-compiled-p definition)) | 2533 | (ad-compiled-p definition)) |
| 2640 | 'function | 2534 | 'function) |
| 2641 | (if (ad-advice-p definition) | 2535 | ((ad-advice-p definition) 'advice))) |
| 2642 | 'advice))))) | ||
| 2643 | 2536 | ||
| 2644 | (defun ad-has-proper-definition (function) | 2537 | (defun ad-has-proper-definition (function) |
| 2645 | "True if FUNCTION is a symbol with a proper definition. | 2538 | "True if FUNCTION is a symbol with a proper definition. |
| @@ -3921,10 +3814,6 @@ undone on exit of this macro." | |||
| 3921 | ;; Use the advice mechanism to advise `documentation' to make it | 3814 | ;; Use the advice mechanism to advise `documentation' to make it |
| 3922 | ;; generate proper documentation strings for advised definitions: | 3815 | ;; generate proper documentation strings for advised definitions: |
| 3923 | 3816 | ||
| 3924 | ;; This makes sure we get the right arglist for `documentation' | ||
| 3925 | ;; during bootstrapping. | ||
| 3926 | (ad-define-subr-args 'documentation '(function &optional raw)) | ||
| 3927 | |||
| 3928 | ;; @@ Starting, stopping and recovering from the advice package magic: | 3817 | ;; @@ Starting, stopping and recovering from the advice package magic: |
| 3929 | ;; =================================================================== | 3818 | ;; =================================================================== |
| 3930 | 3819 | ||
diff --git a/lisp/help-fns.el b/lisp/help-fns.el index 206a9af3a90..97ce7ca44ef 100644 --- a/lisp/help-fns.el +++ b/lisp/help-fns.el | |||
| @@ -99,46 +99,55 @@ ARGLIST can also be t or a string of the form \"(FUN ARG1 ARG2 ...)\"." | |||
| 99 | (format "%S" (help-make-usage 'fn arglist)))))) | 99 | (format "%S" (help-make-usage 'fn arglist)))))) |
| 100 | 100 | ||
| 101 | ;; FIXME: Move to subr.el? | 101 | ;; FIXME: Move to subr.el? |
| 102 | (defun help-function-arglist (def) | 102 | (defun help-function-arglist (def &optional preserve-names) |
| 103 | "Return a formal argument list for the function DEF. | ||
| 104 | IF PRESERVE-NAMES is non-nil, return a formal arglist that uses | ||
| 105 | the same names as used in the original source code, when possible." | ||
| 103 | ;; Handle symbols aliased to other symbols. | 106 | ;; Handle symbols aliased to other symbols. |
| 104 | (if (and (symbolp def) (fboundp def)) (setq def (indirect-function def))) | 107 | (if (and (symbolp def) (fboundp def)) (setq def (indirect-function def))) |
| 105 | ;; If definition is a macro, find the function inside it. | 108 | ;; If definition is a macro, find the function inside it. |
| 106 | (if (eq (car-safe def) 'macro) (setq def (cdr def))) | 109 | (if (eq (car-safe def) 'macro) (setq def (cdr def))) |
| 107 | (cond | 110 | (cond |
| 108 | ((and (byte-code-function-p def) (integerp (aref def 0))) | 111 | ((and (byte-code-function-p def) (listp (aref def 0))) (aref def 0)) |
| 109 | (let* ((args-desc (aref def 0)) | ||
| 110 | (max (lsh args-desc -8)) | ||
| 111 | (min (logand args-desc 127)) | ||
| 112 | (rest (logand args-desc 128)) | ||
| 113 | (arglist ())) | ||
| 114 | (dotimes (i min) | ||
| 115 | (push (intern (concat "arg" (number-to-string (1+ i)))) arglist)) | ||
| 116 | (when (> max min) | ||
| 117 | (push '&optional arglist) | ||
| 118 | (dotimes (i (- max min)) | ||
| 119 | (push (intern (concat "arg" (number-to-string (+ 1 i min)))) | ||
| 120 | arglist))) | ||
| 121 | (unless (zerop rest) (push '&rest arglist) (push 'rest arglist)) | ||
| 122 | (nreverse arglist))) | ||
| 123 | ((byte-code-function-p def) (aref def 0)) | ||
| 124 | ((eq (car-safe def) 'lambda) (nth 1 def)) | 112 | ((eq (car-safe def) 'lambda) (nth 1 def)) |
| 125 | ((eq (car-safe def) 'closure) (nth 2 def)) | 113 | ((eq (car-safe def) 'closure) (nth 2 def)) |
| 126 | ((subrp def) | 114 | ((or (and (byte-code-function-p def) (integerp (aref def 0))) |
| 127 | (let ((arity (subr-arity def)) | 115 | (subrp def)) |
| 128 | (arglist ())) | 116 | (or (when preserve-names |
| 129 | (dotimes (i (car arity)) | 117 | (let* ((doc (condition-case nil (documentation def) (error nil))) |
| 130 | (push (intern (concat "arg" (number-to-string (1+ i)))) arglist)) | 118 | (docargs (if doc (car (help-split-fundoc doc nil)))) |
| 131 | (cond | 119 | (arglist (if docargs |
| 132 | ((not (numberp (cdr arglist))) | 120 | (cdar (read-from-string (downcase docargs))))) |
| 133 | (push '&rest arglist) | 121 | (valid t)) |
| 134 | (push 'rest arglist)) | 122 | ;; Check validity. |
| 135 | ((< (car arity) (cdr arity)) | 123 | (dolist (arg arglist) |
| 136 | (push '&optional arglist) | 124 | (unless (and (symbolp arg) |
| 137 | (dotimes (i (- (cdr arity) (car arity))) | 125 | (let ((name (symbol-name arg))) |
| 138 | (push (intern (concat "arg" (number-to-string | 126 | (if (eq (aref name 0) ?&) |
| 139 | (+ 1 i (car arity))))) | 127 | (memq arg '(&rest &optional)) |
| 140 | arglist)))) | 128 | (not (string-match "\\." name))))) |
| 141 | (nreverse arglist))) | 129 | (setq valid nil))) |
| 130 | (when valid arglist))) | ||
| 131 | (let* ((args-desc (if (not (subrp def)) | ||
| 132 | (aref def 0) | ||
| 133 | (let ((a (subr-arity def))) | ||
| 134 | (logior (car a) | ||
| 135 | (if (numberp (cdr a)) | ||
| 136 | (lsh (cdr a) 8) | ||
| 137 | (lsh 1 7)))))) | ||
| 138 | (max (lsh args-desc -8)) | ||
| 139 | (min (logand args-desc 127)) | ||
| 140 | (rest (logand args-desc 128)) | ||
| 141 | (arglist ())) | ||
| 142 | (dotimes (i min) | ||
| 143 | (push (intern (concat "arg" (number-to-string (1+ i)))) arglist)) | ||
| 144 | (when (> max min) | ||
| 145 | (push '&optional arglist) | ||
| 146 | (dotimes (i (- max min)) | ||
| 147 | (push (intern (concat "arg" (number-to-string (+ 1 i min)))) | ||
| 148 | arglist))) | ||
| 149 | (unless (zerop rest) (push '&rest arglist) (push 'rest arglist)) | ||
| 150 | (nreverse arglist)))) | ||
| 142 | ((and (eq (car-safe def) 'autoload) (not (eq (nth 4 def) 'keymap))) | 151 | ((and (eq (car-safe def) 'autoload) (not (eq (nth 4 def) 'keymap))) |
| 143 | "[Arg list not available until function definition is loaded.]") | 152 | "[Arg list not available until function definition is loaded.]") |
| 144 | (t t))) | 153 | (t t))) |