Commit fabaa9b5 authored by Richard M. Stallman's avatar Richard M. Stallman
Browse files

New handling of automatic advice activation that

exploits modified built-in versions of `fset' and `defalias' which
take care of this functionality directly:
(ad-start-advice-on-load, ad-activate-on-definition)
(ad-definition-hooks, ad-enable-definition-hooks, ad-defined-function)
(ad-advised-definers, ad-advised-byte-compilers, byte-constant)
(byte-constant-limit, byte-constant2, byte-fset)
(ad-byte-code-fset-regexp): Variables deleted.
(ad-activate-defined-function, ad-find-fset-in-byte-code)
(ad-scan-byte-code-for-fsets, ad-advised-byte-code)
(ad-recover-byte-code, ad-enable-definition-hooks)
(ad-disable-definition-hooks): Functions deleted.
(defun, defmacro, fset, defalias, define-function)
(byte-compile-from-buffer, byte-compile-top-level): Removed `defadvice'
for these functions.
(ad-save-real-definitions): Removed saving of `byte-code'.
(ad-activate-off): New dummy function.
(ad-activate-on): New name for `ad-activate'.  All calls changed.
(ad-with-auto-activation-disabled): New macro prevents automatic
advice activation.
(ad-safe-fset): New function, used instead of `ad-real-fset'.
(ad-compile-function): Disable automatic advice activation while
compiling, because `byte-compile' uses `fset'.
(ad-activate-on): Renamed from `ad-activate'.  Avoid recursive calls.
(ad-activate-on-top-level): New variable.
(ad-start-advice, ad-stop-advice, ad-recover-normality): Modified to
achieve de/activation of automatic advice activation by setting the
definition of `ad-activate' to `ad-activate-on' or `ad-activate-off'.
(ad-start-advice): Is now called unconditionally when Advice is loaded.

Made compilation behavior of advised definitions customizable, since
loading the byte-compiler takes some time and is not always worth the
cost, e.g., if one only wants to make a few simple modifications:
(ad-default-compilation-action): New variable which specifies whether
to compile an advised definition in case the COMPILE argument to
`ad-activate-on' or one of its friends was supplied as nil.
(ad-preactivate-advice): Supply negative COMPILE argument to prevent
compilation.
(ad-should-compile): New function.
(ad-activate-advised-definition): Use `ad-should-compile' to determine
whether an advised definition should get compiled.
(ad-activate-on, ad-update, ad-activate-regexp, ad-update-regexp)
(ad-activate-all): Doc fixes.
(ad-update): Leave handling of COMPILE up to `ad-activate-on'.

Extracted construction of freeze-advices from `defadvice':
(ad-make-freeze-definition): New function.
(defadvice): Use `ad-make-freeze-definition' to construct frozen defs.
parent 8951521e
......@@ -4,7 +4,7 @@
;; Author: Hans Chalupsky <hans@cs.buffalo.edu>
;; Created: 12 Dec 1992
;; Version: advice.el,v 2.11 1994/02/24 22:51:43 hans Exp
;; Version: advice.el,v 2.13 1994/08/03 23:27:05 hans Exp
;; Keywords: extensions, lisp, tools
;; This file is part of GNU Emacs.
......@@ -26,7 +26,7 @@
;; LCD Archive Entry:
;; advice|Hans Chalupsky|hans@cs.buffalo.edu|
;; Overloading mechanism for Emacs Lisp functions|
;; 1994/02/24 22:51:43|2.11|~/packages/advice.el.Z|
;; 1994/08/03 23:27:05|2.13|~/packages/advice.el.Z|
;;; Commentary:
......@@ -68,8 +68,7 @@
;; - Advised functions can be byte-compiled either at file-compile time
;; (see preactivation) or activation time.
;; - Separation of advice definition and activation
;; - Provides generally accessible function definition (after) hooks
;; - Forward advice is possible (an application of definition hooks), that is
;; - Forward advice is possible, that is
;; as yet undefined or autoload functions can be advised without having to
;; preload the file in which they are defined.
;; - Forward redefinition is possible because around advice can be used to
......@@ -83,8 +82,6 @@
;; functions depending on what pieces of advice are currently en/disabled
;; - Provides manipulation mechanisms for sets of advised functions via
;; regular expressions that match advice names
;; - Allows definition of load-hooks for arbitrary Emacs Lisp files without
;; modification of these files
;; @ How to get Advice for Emacs-18:
;; =================================
......@@ -114,7 +111,9 @@
;; @ Restrictions:
;; ===============
;; - This version of Advice only works for Emacs-19 or Lucid Emacs.
;; - This version of Advice only works for Emacs 19.26 and later. It uses
;; new versions of the built-in functions `fset/defalias' which are not
;; yet available in Lucid Emacs, hence, it won't work there.
;; - Advised functions/macros/subrs will only exhibit their advised behavior
;; when they are invoked via their function cell. This means that advice will
;; not work for the following:
......@@ -124,13 +123,6 @@
;; + advised macros which were expanded during byte-compilation before
;; their advice was activated.
;; @ Known bug:
;; ============
;; - Using automatic activation of (forward) advice will break the
;; function `interactive-p' when it is used in the body of a `catch'
;; (this problem will go away once automatic advice activation gets
;; supported by built-in functions).
;; @ Credits:
;; ==========
;; This package is an extension and generalization of packages such as
......@@ -151,14 +143,11 @@
;; Before we begin: CAUTION!!
;; Advice provides you with a lot of rope to hang yourself on very
;; easily accessible trees, so, here are a few important things you
;; should know: Once Advice has been started with `ad-start-advice' it
;; generates advised definitions of the `documentation' function, and,
;; if definition hooks are enabled (e.g., for forward advice), also of
;; `defun', `defmacro' and `fset' (if you use Jamie Zawinski's (jwz)
;; optimizing byte-compiler as standardly used in Emacs-19 and
;; Lucid Emacs-19 (Lemacs), then enabling definition hooks will also
;; redefine the `byte-code' subr). All these changes can be undone at
;; any time with `M-x ad-stop-advice'.
;; should know: Once Advice has been started with `ad-start-advice'
;; (which happens automatically when you load this file), it
;; generates an advised definition of the `documentation' function, and
;; it will enable automatic advice activation when functions get defined.
;; All of this can be undone at any time with `M-x ad-stop-advice'.
;;
;; If you experience any strange behavior/errors etc. that you attribute to
;; Advice or to some ill-advised function do one of the following:
......@@ -190,30 +179,17 @@
;; @ Customization:
;; ================
;; Part of the advice magic does not start until you call `ad-start-advice'
;; which you can either do interactively, explicitly in your .emacs, or by
;; putting
;;
;; (setq ad-start-advice-on-load t)
;;
;; into your .emacs which will automatically start advice when the file gets
;; loaded.
;; If you want to be able to forward advise functions, that is to advise them
;; when they are not yet defined or defined as autoloads, then you should put
;; the following into your .emacs
;;
;; (setq ad-activate-on-definition t)
;;
;; which will activate all advice at the time the function gets actually
;; defined/loaded. The value of this variable will not have any effect until
;; `ad-start-advice' gets executed.
;; Look at the documentation of `ad-redefinition-action' for possible values
;; of this variable. Its default value is `warn' which will print a warning
;; message when an already defined advised function gets redefined with a
;; new original definition and de/activated.
;; Look at the documentation of `ad-default-compilation-action' for possible
;; values of this variable. Its default value is `maybe' which will compile
;; advised definitions during activation in case the byte-compiler is already
;; loaded. Otherwise, it will leave them uncompiled.
;; @ Motivation:
;; =============
;; Before I go on explaining how advice works, here are four simple examples
......@@ -575,8 +551,8 @@
;; The advised definition will get compiled either if `ad-activate' was called
;; interactively with a prefix argument, or called explicitly with its second
;; argument as t, or, if this was a case of forward advice if the original
;; definition of the function was compiled. If the advised definition was
;; argument as t, or, if `ad-default-compilation-action' justifies it according
;; to the current system state. If the advised definition was
;; constructed during "preactivation" (see below) then that definition will
;; be already compiled because it was constructed during byte-compilation of
;; the file that contained the `defadvice' with the `preactivate' flag.
......@@ -691,8 +667,8 @@
;; match for the regular expression. To enable ange-ftp again we would use
;; `ad-enable-regexp' and then activate or update again.
;; @@ Forward advice, function definition hooks:
;; =============================================
;; @@ Forward advice, automatic advice activation:
;; ===============================================
;; Because most Emacs Lisp packages are loaded on demand via an autoload
;; mechanism it is essential to be able to "forward advise" functions.
;; Otherwise, proper advice definition and activation would make it necessary
......@@ -706,129 +682,20 @@
;; Advice implements forward advice mainly via the following: 1) Separation
;; of advice definition and activation that makes it possible to accumulate
;; advice information without having the original function already defined,
;; 2) special versions of the function defining functions `defun', `defmacro'
;; and `fset' that check for advice information whenever they define a
;; function. If advice information was found and forward advice is enabled
;; then the advice will immediately get activated when the function gets
;; defined.
;; 2) special versions of the built-in functions `fset/defalias' which check
;; for advice information whenever they define a function. If advice
;; information was found then the advice will immediately get activated when
;; the function gets defined.
;; @@@ Enabling forward advice:
;; ============================
;; Forward advice is enabled by setting `ad-activate-on-definition' to t
;; and then calling `ad-start-advice' which can either be done interactively,
;; directly with `(ad-start-advice)' in your .emacs, or by setting
;; `ad-start-advice-on-load' to t before advice gets loaded. For example,
;; putting the following into your .emacs will enable forward advice:
;;
;; (setq ad-start-advice-on-load t)
;; (setq ad-activate-on-definition t)
;;
;; "Activation on definition" means, that whenever a function gets defined
;; Automatic advice activation means, that whenever a function gets defined
;; with either `defun', `defmacro', `fset' or by loading a byte-compiled
;; file, and the function has some advice-info stored with it then that
;; advice will get activated right away.
;; If jwz's byte-compiler is used then `ad-use-jwz-byte-compiler' should
;; be t in order to make forward advice work with functions defined in
;; compiled files generated by that compiler. In v19s which use this
;; compiler the value of this variable will be correct automatically.
;; If you use a v18 Emacs in conjunction with jwz's compiler and you want
;; to use forward advice then you should check its value after loading
;; advice. If it is nil set it explicitly with
;;
;; (setq ad-use-jwz-byte-compiler t)
;;
;; along with `ad-activate-on-definition' before you start advice (see above).
;; IMPORTANT: A v18 Emacs + jwz's compiler + forward advice means performance
;; tradeoffs which are described below.
;; @@@ Forward advice with compiled files generated by jwz's byte-compiler:
;; ========================================================================
;; The v18 byte-compiler only uses `defun/defmacro' to define compiled
;; functions, hence, providing advised versions of these functions was
;; sufficient to achieve forward advice. With the advent of Jamie Zawinski's
;; optimizing byte-compiler which is now standardly used in Emacs-19 and
;; Lemacs things became more complicated. jwz's compiler defines functions
;; in hunks of byte-code without explicit usage of `defun/defmacro'. To
;; still provide forward advice even in this scenario, advice defines an
;; advised version of the `byte-code' subr that scans its arguments for
;; function definitions during the loading of compiled files. While this is
;; no problem in a v19 Emacs, because it uses a new datatype for compiled
;; code objects and the `byte-code' subr is only rarely used at all, it
;; presents a major problem in a v18 Emacs because there calls to
;; `byte-code' are the only means of executing compiled code (every body of
;; a compiled function contains a call to `byte-code'). Because the advised
;; `byte-code' has to perform some extra checks every call to a compiled
;; function becomes more expensive.
;; Enabling forward advice leads to performance degradation in the following
;; situations:
;; - A v18 Emacs is used and the value of `ad-use-jwz-byte-compiler' is t
;; (either because jwz's byte-compiler is used instead of the standard v18
;; compiler, or some compiled files generated by jwz's compiler are used).
;; - A v19 Emacs is used with some old-style v18 compiled files.
;; Some performance experiments I conducted showed that function call intensive
;; code (such as the highly recursive byte-compiler itself) slows down by a
;; factor of 1.8. Function call intensive code that runs while a file gets
;; loaded can slow down by a factor of 6! For the v19 scenario this performance
;; lossage would only apply to code that was loaded from old v18 compiled
;; files.
;; MORAL: If you use a v18 Emacs in conjunction with jwz's byte-compiler you
;; should think twice whether you really need forward advice. There are some
;; alternatives to forward advice described below that might give you what
;; you need without the loss of performance (that performance loss probably
;; outweighs by far any performance gain due to the optimizing nature of jwz's
;; compiler).
;; @@@ Alternatives to automatic activation of forward advice:
;; ===========================================================
;; If you use a v18 Emacs in conjunction with jwz's compiler, or you simply
;; don't trust the automatic activation mechanism of forward advice, then
;; you can use some of the following alternatives to get around that:
;; - Preload the file that contains the definition of the function that you
;; want to advice. Inelegant and wasteful, but it works.
;; - If the package that contains the definition of the function you want to
;; advise has any mode hooks, and the advised function is only used once such
;; a mode has been entered, then you can activate the advice in the mode
;; hook. Just put a form like `(ad-activate 'my-advised-fn t)' into the
;; hook definition. The caching mechanism will reuse advised definitions,
;; so calling that mode hook over and over again will not construct
;; advised definitions over and over again, so you won't loose any
;; performance.
;; - If your Emacs comes with file load hooks (such as v19's
;; `after-load-alist' mechanism), then you can put the activation form
;; into that, for example, add `("myfile" (ad-activate 'my-advised-fn t))'
;; to it to activate the advice right ater "myfile" got loaded.
;; @@@ Function definition hooks:
;; ==============================
;; Automatic activation of forward advice is implemented as an application
;; of a more general function definition hook mechanism. After a function
;; gets re/defined with `defun/defmacro/fset' or via a hunk of byte-code
;; during the loading of a byte-compiled file, and function definition hooks
;; are enabled, then all hook functions stored in `ad-definition-hooks' are
;; run with the variable `ad-defined-function' bound to the name of the
;; currently defined function.
;; Function definition hooks can be enabled with
;;
;; (setq ad-enable-definition-hooks t)
;;
;; before advice gets started with `ad-start-advice'. Setting
;; `ad-activate-on-definition' to t automatically enables definition hooks
;; regardless of the value of `ad-enable-definition-hooks'.
;; @@@ Wish list:
;; ==============
;; - The implementation of definition hooks for v19 compiled files would be
;; safer if jwz's byte-compiler used something like `byte-code-tl' instead
;; of `byte-code' to execute hunks of function defining byte-code at the
;; top level of compiled files.
;; - Definition hooks should be implemented directly as part of the C-code
;; that implements `fset', because then Advice wouldn't have to use all
;; these dirty hacks to achieve this functionality.
;; @@@ Enabling automatic advice activation:
;; =========================================
;; Automatic advice activation is enabled by default. It can be disabled by
;; doint `M-x ad-stop-advice' and enabled again with `M-x ad-start-advice'.
;; @@ Caching of advised definitions:
;; ==================================
......@@ -1954,34 +1821,20 @@
(require 'advice-preload "advice.el")
;; @@ Variable definitions:
;; ========================
(defconst ad-version "2.11")
(defmacro ad-lemacs-p ()
;;Expands into Non-nil constant if we run Lucid's version of Emacs-19.
;;Unselected conditional code will be optimized away during compilation.
(string-match "Lucid" emacs-version))
;;;###autoload
(defvar ad-start-advice-on-load t
"*Non-nil will start Advice magic when this file gets loaded.
Also see function `ad-start-advice'.")
;;;###autoload
(defvar ad-activate-on-definition nil
"*Non-nil means automatic advice activation at function definition.
Set this variable to t if you want to enable forward advice (which is
automatic advice activation of a previously undefined function at the
point the function gets defined/loaded/autoloaded). The value of this
variable takes effect only during the execution of `ad-start-advice'.
If non-nil it will enable definition hooks regardless of the value
of `ad-enable-definition-hooks'.")
;; @@ Variable definitions:
;; ========================
(defconst ad-version "2.13")
;;;###autoload
(defvar ad-redefinition-action 'warn
"*Defines what to do with redefinitions during de/activation.
"*Defines what to do with redefinitions during Advice de/activation.
Redefinition occurs if a previously activated function that already has an
original definition associated with it gets redefined and then de/activated.
In such a case we can either accept the current definition as the new
......@@ -1992,16 +1845,14 @@ it additionally prints a warning message. All other values will be
interpreted as `error'.")
;;;###autoload
(defvar ad-definition-hooks nil
"*List of hooks to be run after a function definition.
The variable `ad-defined-function' will be bound to the name of
the currently defined function when the hook function is run.")
;;;###autoload
(defvar ad-enable-definition-hooks nil
"*Non-nil will enable hooks to be run on function definition.
Setting this variable is a noop unless the value of
`ad-activate-on-definition' (which see) is nil.")
(defvar ad-default-compilation-action 'maybe
"*Defines whether to compile advised definitions during activation.
A value of `always' will result in unconditional compilation, `never' will
always avoid compilation, `maybe' will compile if the byte-compiler is already
loaded, and `like-original' will compile if the original definition of the
advised function is compiled or a built-in function. Every other value will
be interpreted as `maybe'. This variable will only be considered if the
COMPILE argument of `ad-activate' was supplied as nil.")
;; @@ Some utilities:
......@@ -2100,9 +1951,7 @@ exited prematurely with `(ad-do-return [VALUE])'."
;; properties into the compiled version of this function such that the
;; proper values will be available at runtime without loading the compiler:
(ad-save-real-definition fset)
(ad-save-real-definition documentation)
(ad-save-real-definition byte-code)
(put 'ad-real-byte-code 'byte-compile nil))
(ad-save-real-definition documentation))
(ad-save-real-definitions)
......@@ -2263,6 +2112,55 @@ either t or nil, and DEFINITION should be a list of the form
(reverse enabled-advices)))
;; @@ Dealing with automatic advice activation via `fset/defalias':
;; ================================================================
;; Since Emacs 19.26 the built-in versions of `fset' and `defalias'
;; take care of automatic advice activation, hence, we don't have to
;; hack it anymore by advising `fset/defun/defmacro/byte-code/etc'.
;; The functionality of the new `fset' is as follows:
;;
;; fset(sym,newdef)
;; assign NEWDEF to SYM
;; if (get SYM 'ad-advice-info)
;; ad-activate(SYM, nil)
;; return (symbol-function SYM)
;;
;; Whether advised definitions created by automatic activations will be
;; compiled depends on the value of `ad-default-compilation-action'.
;; Since calling `ad-activate' in the built-in definition of `fset' can
;; create major disasters we have to be a bit careful. One precaution is
;; to provide a dummy definition for `ad-activate' which can be used to
;; turn off automatic advice activation (e.g., when `ad-stop-advice' or
;; `ad-recover-normality' are called). Another is to avoid recursive calls
;; to `ad-activate-on' by using `ad-with-auto-activation-disabled' where
;; appropriate, especially in a safe version of `fset'.
;; For now define `ad-activate' to the dummy definition:
(defun ad-activate (function &optional compile)
"Automatic advice activation is disabled. `ad-start-advice' enables it."
nil)
;; This is just a copy of the above:
(defun ad-activate-off (function &optional compile)
"Automatic advice activation is disabled. `ad-start-advice' enables it."
nil)
;; This will be t for top-level calls to `ad-activate-on':
(defvar ad-activate-on-top-level t)
(defmacro ad-with-auto-activation-disabled (&rest body)
(` (let ((ad-activate-on-top-level nil))
(,@ body))))
(defun ad-safe-fset (symbol definition)
;; A safe `fset' which will never call `ad-activate' recursively.
(ad-with-auto-activation-disabled
(ad-real-fset symbol definition)))
;; @@ Access functions for original definitions:
;; ============================================
;; The advice-info of an advised function contains its `origname' which is
......@@ -2282,7 +2180,7 @@ either t or nil, and DEFINITION should be a list of the form
(symbol-function origname)))))
(defmacro ad-set-orig-definition (function definition)
(` (ad-real-fset
(` (ad-safe-fset
(ad-get-advice-info-field function 'origname) (, definition))))
(defmacro ad-clear-orig-definition (function)
......@@ -2598,7 +2496,7 @@ will clear the cache."
;; (compiled-function-p is an obsolete function in Emacs):
(if (and (not (fboundp 'byte-code-function-p))
(fboundp 'compiled-function-p))
(ad-real-fset 'byte-code-function-p 'compiled-function-p))
(ad-safe-fset 'byte-code-function-p 'compiled-function-p))
(defmacro ad-compiled-p (definition)
;;"non-nil if DEFINITION is a compiled byte-code object."
......@@ -2777,7 +2675,10 @@ will clear the cache."
"Byte-compiles FUNCTION (or macro) if it is not yet compiled."
(interactive "aByte-compile function: ")
(if (ad-is-compilable function)
(byte-compile function)))
;; Need to turn off auto-activation
;; because `byte-compile' uses `fset':
(ad-with-auto-activation-disabled
(byte-compile function))))
;; @@ Constructing advised definitions:
......@@ -3469,7 +3370,7 @@ advised definition from scratch."
(ad-add-advice function advice class position)
(ad-enable-advice function class (ad-advice-name advice))
(ad-clear-cache function)
(ad-activate function nil)
(ad-activate-on function -1)
(if (and (ad-is-active function)
(ad-get-cache-definition function))
(list (ad-get-cache-definition function)
......@@ -3477,20 +3378,129 @@ advised definition from scratch."
(ad-set-advice-info function old-advice-info)
;; Don't `fset' function to nil if it was previously unbound:
(if function-defined-p
(ad-real-fset function old-definition)
(ad-safe-fset function old-definition)
(fmakunbound function)))))
;; @@ Freezing:
;; ============
;; Freezing transforms a `defadvice' into a redefining `defun/defmacro'
;; for the advised function without keeping any advice information. This
;; feature was jwz's idea: It generates a dumpable function definition
;; whose documentation can be written to the DOC file, and the generated
;; code does not need any Advice runtime support. Of course, frozen advices
;; cannot be undone.
;; Freezing only considers the advice of the particular `defadvice', other
;; already existing advices for the same function will be ignored. To ensure
;; proper interaction when an already advised function gets redefined with
;; a frozen advice, frozen advices always use the actual original definition
;; of the function, i.e., they are always at the core of the onion. E.g., if
;; an already advised function gets redefined with a frozen advice and then
;; unadvised, the frozen advice remains as the new definition of the function.
;; While multiple freeze advices for a single function or freeze-advising
;; of an already advised function are possible, they are better avoided,
;; because definition/compile/load ordering is relevant, and it becomes
;; incomprehensible pretty quickly.
(defun ad-make-freeze-definition (function advice class position)
(if (not (ad-has-proper-definition function))
(error
"ad-make-freeze-definition: `%s' is not yet defined"
function))
(let* ((name (ad-advice-name advice))
;; With a unique origname we can have multiple freeze advices
;; for the same function, each overloading the previous one:
(unique-origname
(intern (format "%s-%s-%s" (ad-make-origname function) class name)))
(orig-definition
;; If FUNCTION is already advised, we'll use its current origdef
;; as the original definition of the frozen advice:
(or (ad-get-orig-definition function)
(symbol-function function)))
(old-advice-info
(if (ad-is-advised function)
(ad-copy-advice-info function)))
(real-docstring-fn
(symbol-function 'ad-make-advised-definition-docstring))
(real-origname-fn
(symbol-function 'ad-make-origname))
(frozen-definition
(unwind-protect
(progn
;; Make sure we construct a proper docstring:
(ad-safe-fset 'ad-make-advised-definition-docstring
'ad-make-freeze-docstring)
;; Make sure `unique-origname' is used as the origname:
(ad-safe-fset 'ad-make-origname '(lambda (x) unique-origname))
;; No we reset all current advice information to nil and
;; generate an advised definition that's solely determined
;; by ADVICE and the current origdef of FUNCTION:
(ad-set-advice-info function nil)
(ad-add-advice function advice class position)
;; The following will provide proper real docstrings as
;; well as a definition that will make the compiler happy:
(ad-set-orig-definition function orig-definition)
(ad-make-advised-definition function))
;; Restore the old advice state:
(ad-set-advice-info function old-advice-info)
;; Restore functions:
(ad-safe-fset
'ad-make-advised-definition-docstring real-docstring-fn)
(ad-safe-fset 'ad-make-origname real-origname-fn))))
(if frozen-definition
(let* ((macro-p (ad-macro-p frozen-definition))
(body (cdr (if macro-p
(ad-lambdafy frozen-definition)
frozen-definition))))
(` (progn
(if (not (fboundp '(, unique-origname)))
(fset '(, unique-origname)
;; avoid infinite recursion in case the function
;; we want to freeze is already advised:
(or (ad-get-orig-definition '(, function))
(symbol-function '(, function)))))
((, (if macro-p 'defmacro 'defun))
(, function)
(,@ body))))))))
;; @@ Activation and definition handling:
;; ======================================
(defun ad-should-compile (function compile)
;;"Returns non-nil if the advised FUNCTION should be compiled.
;;If COMPILE is non-nil and not a negative number then it returns t.
;;If COMPILE is a negative number then it returns nil.
;;If COMPILE is nil then the result depends on the value of
;;`ad-default-compilation-action' (which see)."
(if (integerp compile)
(>= compile 0)
(if compile
compile
(cond ((eq ad-default-compilation-action 'never)
nil)
((eq ad-default-compilation-action 'always)
t)
((eq ad-default-compilation-action 'like-original)
(or (ad-subr-p (ad-get-orig-definition function))
(ad-compiled-p (ad-get-orig-definition function))))
;; everything else means `maybe':
(t (featurep 'byte-compile))))))
(defun ad-activate-advised-definition (function compile)
;;"Redefines FUNCTION with its advised definition from cache or scratch.
;;If COMPILE is true the resulting FUNCTION will be compiled. The current
;;definition and its cache-id will be put into the cache."
;;The resulting FUNCTION will be compiled if `ad-should-compile' returns t.
;;The current definition and its cache-id will be put into the cache."
(let ((verified-cached-definition
(if (ad-verify-cache-id function)
(ad-get-cache-definition function))))
(ad-real-fset function
(ad-safe-fset function
(or verified-cached-definition
(ad-make-advised-definition function)))
(if compile (ad-compile-function function))
(if (ad-should-compile function compile)
(ad-compile-function function))
(if verified-cached-definition
(if (not (eq verified-cached-definition (symbol-function function)))
;; we must have compiled, cache the compiled definition:
......@@ -3528,7 +3538,7 @@ the value of `ad-redefinition-action' and de/activate again."
(error "ad-handle-definition (see its doc): `%s' %s"
function "illegally redefined")
(if (eq ad-redefinition-action 'discard)
(ad-real-fset function original-definition)
(ad-safe-fset function original-definition)
(ad-set-orig-definition function current-definition)
(if (eq ad-redefinition-action 'warn)
(message "ad-handle-definition: `%s' got redefined"
......@@ -3547,37 +3557,43 @@ the value of `ad-redefinition-action' and de/activate again."
;; @@ The top-level advice interface:
;; ==================================
(defun ad-activate (function &optional compile)
(defun ad-activate-on (function &optional compile)
"Activates all the advice information of an advised FUNCTION.
If FUNCTION has a proper original definition then an advised
definition will be generated from FUNCTION's advice info and the
definition of FUNCTION will be replaced with it. If a previously
cached advised definition was available, it will be used. With an
argument (COMPILE is non-nil) the resulting function (or a compilable
cached definition) will also be compiled. Activation of an advised
function that has an advice info but no actual pieces of advice is
equivalent to a call to `ad-unadvise'. Activation of an advised
function that has actual pieces of advice but none of them are enabled
is equivalent to a call to `ad-deactivate'. The current advised
cached advised definition was available, it will be used.
The optional COMPILE argument determines whether the resulting function
or a compilable cached definition will be compiled. If it is negative
no compilation will be performed, if it is positive or otherwise non-nil
the resulting function will be compiled, if it is nil the behavior depends
on the value of `ad-default-compilation-action' (which see).
Activation of an advised function that has an advice info but no actual
pieces of advice is equivalent to a call to `ad-unadvise'. Activation of
an advised function that has actual pieces of advice but none of them are
enabled is equivalent to a call to `ad-deactivate'. The current advised
definition will always be cached for later usage."
(interactive
(list (ad-read-advised-function "Activate advice of: ")
current-prefix-arg))
(if (not (ad-is-advised function))
(error "ad-activate: `%s' is not advised" function)
(ad-handle-definition function)
;; Just return for forward advised and not yet defined functions:
(if (ad-get-orig-definition function)
(if (not (ad-has-any-advice function))
(ad-unadvise function)
;; Otherwise activate the advice:
(cond ((ad-has-redefining-advice function)
(ad-activate-advised-definition function compile)
(ad-set-advice-info-field function 'active t)
(eval (ad-make-hook-form function 'activation))
function)
;; Here we are if we have all disabled advices:
(t (ad-deactivate function)))))))
(if ad-activate-on-top-level
;; avoid recursive calls to `ad-activate-on':
(ad-with-auto-activation-disabled