aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2011-04-13 14:56:47 -0300
committerStefan Monnier2011-04-13 14:56:47 -0300
commitc2bd2ab02856f36d41c88f5e054f4444a6366d5e (patch)
treef4b359472e89a3b2012f88f2c860a295652e8bcf
parentc0ece6a5c4c8dc87be1da6808289c88de19d8398 (diff)
downloademacs-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/ChangeLog11
-rw-r--r--lisp/emacs-lisp/advice.el141
-rw-r--r--lisp/help-fns.el75
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 @@
12011-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
12011-04-13 Juanma Barranquero <lekktu@gmail.com> 122011-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."
2536If DEFINITION could be from a subr then its NAME should be 2471If DEFINITION could be from a subr then its NAME should be
2537supplied to make subr arglist lookup more efficient." 2472supplied 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.
2557Either use the one stored under the `ad-subr-arglist' property,
2558or try to retrieve it from the docstring and cache it under
2559that 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.
104IF PRESERVE-NAMES is non-nil, return a formal arglist that uses
105the 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)))