diff options
| author | Stefan Monnier | 2022-01-31 11:07:06 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2022-01-31 11:07:26 -0500 |
| commit | 1d1b664fbb9232aa40d8daa54a689cfd63d38aa9 (patch) | |
| tree | 164ad10242c8566c3c8fa1d0c12c50804f82d791 | |
| parent | 90bbf27f02b1e7bf9cc0f0206313795c210c565b (diff) | |
| download | emacs-1d1b664fbb9232aa40d8daa54a689cfd63d38aa9.tar.gz emacs-1d1b664fbb9232aa40d8daa54a689cfd63d38aa9.zip | |
(function-history): New symbol property (bug#53632)
Rework the code we have in Fdefalias that tries to keep track
of definitions so as to be able to undo them later.
We used to store in `load-history` when an autoload is redefined as
a non-autoload and in the `autoload` symbol property we used to store
the autoload data that used to be used before it got overriden.
Instead, store the history of the function definition of
a symbol in its `function-history` symbol property.
To make this list cheap in the default case, the latest value is not stored
in the list (since it's in the `symbol-function`) and neither is the first
file. So if there's only been a single definition (the most common case),
the list is empty and the property is just not present at all.
The patch also gets rid of the `autoload` vs `defun` distinction in
`load-history` which seems unnecessary (a significant part of the
motivation for this patch was to get rid of the special handling of
autoloads in this part of the code).
* src/data.c (add_to_function_history): New function.
(defalias): Use it. Don't add the `t` entries for autoloads and always
use `defun` regardless of the kind of definition.
Change `Vautoload_queue` to only hold the function
symbols since the rest is now available from `function-history`.
* src/eval.c (un_autoload): Adjust accordingly.
* src/lread.c (load-history): Udate docstring.
* lisp/loadhist.el (loadhist-unload-filename): New var.
(unload-feature): Bind it.
(loadhist-unload-element): Document its availability.
(loadhist--restore-autoload): Delete var.
(loadhist--unload-function): Delete function.
(loadhist-unload-element): Delete the `t` and `autoload` methods.
Rewrite the `defun` method using `function-history`.
* lisp/help-fns.el: Require `seq`.
(help-fns--autoloaded-p): Rewrite.
(help-fns-function-description-header): Adjust call accordingly.
* doc/lispref/loading.texi (Where Defined): Remove `autoload` and `t`
entries from `load-history` since we don't generate them any more.
Document the `function-history` which replaces the `autoload` property.
(Unloading): Adjust symbol property name accordingly.
* test/lisp/loadhist-resources/loadhist--bar.el:
* test/lisp/loadhist-resources/loadhist--foo.el: New files.
* test/lisp/loadhist-tests.el (loadhist-tests-unload-feature-nested)
(loadhist-tests-unload-feature-notnested): New tests.
| -rw-r--r-- | doc/lispref/loading.texi | 24 | ||||
| -rw-r--r-- | etc/NEWS | 6 | ||||
| -rw-r--r-- | lisp/help-fns.el | 23 | ||||
| -rw-r--r-- | lisp/loadhist.el | 54 | ||||
| -rw-r--r-- | src/data.c | 62 | ||||
| -rw-r--r-- | src/eval.c | 14 | ||||
| -rw-r--r-- | src/lread.c | 9 | ||||
| -rw-r--r-- | test/lisp/loadhist-resources/loadhist--bar.el | 27 | ||||
| -rw-r--r-- | test/lisp/loadhist-resources/loadhist--foo.el | 29 | ||||
| -rw-r--r-- | test/lisp/loadhist-tests.el | 47 |
10 files changed, 218 insertions, 77 deletions
diff --git a/doc/lispref/loading.texi b/doc/lispref/loading.texi index 5957b8ac385..6179270dd3d 100644 --- a/doc/lispref/loading.texi +++ b/doc/lispref/loading.texi | |||
| @@ -1067,13 +1067,8 @@ list elements have these forms: | |||
| 1067 | The symbol @var{var} was defined as a variable. | 1067 | The symbol @var{var} was defined as a variable. |
| 1068 | @item (defun . @var{fun}) | 1068 | @item (defun . @var{fun}) |
| 1069 | The function @var{fun} was defined. | 1069 | The function @var{fun} was defined. |
| 1070 | @item (t . @var{fun}) | ||
| 1071 | The function @var{fun} was previously an autoload before this library | ||
| 1072 | redefined it as a function. The following element is always | ||
| 1073 | @code{(defun . @var{fun})}, which represents defining @var{fun} as a | 1070 | @code{(defun . @var{fun})}, which represents defining @var{fun} as a |
| 1074 | function. | 1071 | function. |
| 1075 | @item (autoload . @var{fun}) | ||
| 1076 | The function @var{fun} was defined as an autoload. | ||
| 1077 | @item (defface . @var{face}) | 1072 | @item (defface . @var{face}) |
| 1078 | The face @var{face} was defined. | 1073 | The face @var{face} was defined. |
| 1079 | @item (require . @var{feature}) | 1074 | @item (require . @var{feature}) |
| @@ -1096,6 +1091,23 @@ The value of @code{load-history} may have one element whose @sc{car} is | |||
| 1096 | by adding the symbols defined to the element for the file being visited, | 1091 | by adding the symbols defined to the element for the file being visited, |
| 1097 | rather than replacing that element. @xref{Eval}. | 1092 | rather than replacing that element. @xref{Eval}. |
| 1098 | 1093 | ||
| 1094 | In addition to @code{load-history}, every function keeps track of its | ||
| 1095 | own history in the symbol property @code{function-history}. | ||
| 1096 | The reason why functions are treated specially in this respect is that | ||
| 1097 | it is common for functions to be defined in two steps in two different | ||
| 1098 | files (typically, one of them is an autoload), so in order to be | ||
| 1099 | able to properly @emph{unload} a file, we need to know more precisely | ||
| 1100 | what that file did to the function definition. | ||
| 1101 | |||
| 1102 | @kindex{function-history} | ||
| 1103 | The symbol property @code{function-history} holds a list of the form | ||
| 1104 | @code{(@var{file1} @var{def2} @var{file2} @var{def3} ...)} where | ||
| 1105 | @var{file1} is the last file that changed the definition and | ||
| 1106 | @var{def2} was the definition before @var{file1}, set by @var{file2}, | ||
| 1107 | etc... Logically this list should end with the name of the first file | ||
| 1108 | that defined this function, but to save space this last element | ||
| 1109 | is usually omitted. | ||
| 1110 | |||
| 1099 | @node Unloading | 1111 | @node Unloading |
| 1100 | @section Unloading | 1112 | @section Unloading |
| 1101 | @cindex unloading packages | 1113 | @cindex unloading packages |
| @@ -1110,7 +1122,7 @@ It undefines all functions, macros, and variables defined in that | |||
| 1110 | library with @code{defun}, @code{defalias}, @code{defsubst}, | 1122 | library with @code{defun}, @code{defalias}, @code{defsubst}, |
| 1111 | @code{defmacro}, @code{defconst}, @code{defvar}, and @code{defcustom}. | 1123 | @code{defmacro}, @code{defconst}, @code{defvar}, and @code{defcustom}. |
| 1112 | It then restores any autoloads formerly associated with those symbols. | 1124 | It then restores any autoloads formerly associated with those symbols. |
| 1113 | (Loading saves these in the @code{autoload} property of the symbol.) | 1125 | (Loading saves these in the @code{function-history} property of the symbol.) |
| 1114 | 1126 | ||
| 1115 | Before restoring the previous definitions, @code{unload-feature} runs | 1127 | Before restoring the previous definitions, @code{unload-feature} runs |
| 1116 | @code{remove-hook} to remove functions defined by the library from certain | 1128 | @code{remove-hook} to remove functions defined by the library from certain |
| @@ -129,6 +129,9 @@ example, if point is before an Emoji sequence, pressing <Delete> will | |||
| 129 | delete the entire sequence, not just a single character at its | 129 | delete the entire sequence, not just a single character at its |
| 130 | beginning. | 130 | beginning. |
| 131 | 131 | ||
| 132 | ** 'load-history' does not treat autoloads specially any more. | ||
| 133 | An autoload definition appears just as a (defun . NAME) and the | ||
| 134 | (t . NAME) entries are not generated any more. | ||
| 132 | 135 | ||
| 133 | * Changes in Emacs 29.1 | 136 | * Changes in Emacs 29.1 |
| 134 | 137 | ||
| @@ -1471,6 +1474,9 @@ The property ':position' now specifies the position of the underline | |||
| 1471 | when used as part of a property list specification for the | 1474 | when used as part of a property list specification for the |
| 1472 | ':underline' attribute. | 1475 | ':underline' attribute. |
| 1473 | 1476 | ||
| 1477 | ** 'defalias' records a more precise history of definitions. | ||
| 1478 | This is recorded in the `function-history` symbol property. | ||
| 1479 | |||
| 1474 | 1480 | ||
| 1475 | * Changes in Emacs 29.1 on Non-Free Operating Systems | 1481 | * Changes in Emacs 29.1 on Non-Free Operating Systems |
| 1476 | 1482 | ||
diff --git a/lisp/help-fns.el b/lisp/help-fns.el index 98a1b11e088..36c7966919b 100644 --- a/lisp/help-fns.el +++ b/lisp/help-fns.el | |||
| @@ -33,6 +33,7 @@ | |||
| 33 | ;;; Code: | 33 | ;;; Code: |
| 34 | 34 | ||
| 35 | (require 'cl-lib) | 35 | (require 'cl-lib) |
| 36 | (require 'seq) | ||
| 36 | (require 'help-mode) | 37 | (require 'help-mode) |
| 37 | (require 'radix-tree) | 38 | (require 'radix-tree) |
| 38 | (eval-when-compile (require 'subr-x)) ;For when-let. | 39 | (eval-when-compile (require 'subr-x)) ;For when-let. |
| @@ -678,19 +679,9 @@ suitable file is found, return nil." | |||
| 678 | (terpri))) | 679 | (terpri))) |
| 679 | 680 | ||
| 680 | ;; We could use `symbol-file' but this is a wee bit more efficient. | 681 | ;; We could use `symbol-file' but this is a wee bit more efficient. |
| 681 | (defun help-fns--autoloaded-p (function file) | 682 | (defun help-fns--autoloaded-p (function) |
| 682 | "Return non-nil if FUNCTION has previously been autoloaded. | 683 | "Return non-nil if FUNCTION has previously been autoloaded." |
| 683 | FILE is the file where FUNCTION was probably defined." | 684 | (seq-some #'autoloadp (get function 'function-history))) |
| 684 | (let* ((file (file-name-sans-extension (file-truename file))) | ||
| 685 | (load-hist load-history) | ||
| 686 | (target (cons t function)) | ||
| 687 | found) | ||
| 688 | (while (and load-hist (not found)) | ||
| 689 | (and (stringp (caar load-hist)) | ||
| 690 | (equal (file-name-sans-extension (caar load-hist)) file) | ||
| 691 | (setq found (member target (cdar load-hist)))) | ||
| 692 | (setq load-hist (cdr load-hist))) | ||
| 693 | found)) | ||
| 694 | 685 | ||
| 695 | (defun help-fns--interactive-only (function) | 686 | (defun help-fns--interactive-only (function) |
| 696 | "Insert some help blurb if FUNCTION should only be used interactively." | 687 | "Insert some help blurb if FUNCTION should only be used interactively." |
| @@ -873,13 +864,13 @@ Returns a list of the form (REAL-FUNCTION DEF ALIASED REAL-DEF)." | |||
| 873 | "Print a line describing FUNCTION to `standard-output'." | 864 | "Print a line describing FUNCTION to `standard-output'." |
| 874 | (pcase-let* ((`(,_real-function ,def ,aliased ,real-def) | 865 | (pcase-let* ((`(,_real-function ,def ,aliased ,real-def) |
| 875 | (help-fns--analyze-function function)) | 866 | (help-fns--analyze-function function)) |
| 876 | (file-name (find-lisp-object-file-name function (if aliased 'defun | 867 | (file-name (find-lisp-object-file-name |
| 877 | def))) | 868 | function (if aliased 'defun def))) |
| 878 | (beg (if (and (or (byte-code-function-p def) | 869 | (beg (if (and (or (byte-code-function-p def) |
| 879 | (keymapp def) | 870 | (keymapp def) |
| 880 | (memq (car-safe def) '(macro lambda closure))) | 871 | (memq (car-safe def) '(macro lambda closure))) |
| 881 | (stringp file-name) | 872 | (stringp file-name) |
| 882 | (help-fns--autoloaded-p function file-name)) | 873 | (help-fns--autoloaded-p function)) |
| 883 | (concat | 874 | (concat |
| 884 | "an autoloaded " (if (commandp def) | 875 | "an autoloaded " (if (commandp def) |
| 885 | "interactive ")) | 876 | "interactive ")) |
diff --git a/lisp/loadhist.el b/lisp/loadhist.el index 48058f40535..39481ab0684 100644 --- a/lisp/loadhist.el +++ b/lisp/loadhist.el | |||
| @@ -157,38 +157,35 @@ documentation of `unload-feature' for details.") | |||
| 157 | ;; mode, or proposed is not nil and not major-mode, and so we use it. | 157 | ;; mode, or proposed is not nil and not major-mode, and so we use it. |
| 158 | (funcall (or proposed 'fundamental-mode))))))) | 158 | (funcall (or proposed 'fundamental-mode))))))) |
| 159 | 159 | ||
| 160 | (defvar loadhist-unload-filename nil) | ||
| 161 | |||
| 160 | (cl-defgeneric loadhist-unload-element (x) | 162 | (cl-defgeneric loadhist-unload-element (x) |
| 161 | "Unload an element from the `load-history'." | 163 | "Unload an element from the `load-history'. |
| 164 | The variable `loadhist-unload-filename' holds the name of the file we're | ||
| 165 | unloading." | ||
| 162 | (message "Unexpected element %S in load-history" x)) | 166 | (message "Unexpected element %S in load-history" x)) |
| 163 | 167 | ||
| 164 | ;; In `load-history', the definition of a previously autoloaded | ||
| 165 | ;; function is represented by 2 entries: (t . SYMBOL) comes before | ||
| 166 | ;; (defun . SYMBOL) and says we should restore SYMBOL's autoload when | ||
| 167 | ;; we undefine it. | ||
| 168 | ;; So we use this auxiliary variable to keep track of the last (t . SYMBOL) | ||
| 169 | ;; that occurred. | ||
| 170 | (defvar loadhist--restore-autoload nil | ||
| 171 | "If non-nil, is a symbol for which to try to restore a previous autoload.") | ||
| 172 | |||
| 173 | (cl-defmethod loadhist-unload-element ((x (head t))) | ||
| 174 | (setq loadhist--restore-autoload (cdr x))) | ||
| 175 | |||
| 176 | (defun loadhist--unload-function (x) | ||
| 177 | (let ((fun (cdr x))) | ||
| 178 | (when (fboundp fun) | ||
| 179 | (when (fboundp 'ad-unadvise) | ||
| 180 | (ad-unadvise fun)) | ||
| 181 | (let ((aload (get fun 'autoload))) | ||
| 182 | (defalias fun | ||
| 183 | (if (and aload (eq fun loadhist--restore-autoload)) | ||
| 184 | (cons 'autoload aload) | ||
| 185 | nil))))) | ||
| 186 | (setq loadhist--restore-autoload nil)) | ||
| 187 | |||
| 188 | (cl-defmethod loadhist-unload-element ((x (head defun))) | 168 | (cl-defmethod loadhist-unload-element ((x (head defun))) |
| 189 | (loadhist--unload-function x)) | 169 | (let* ((fun (cdr x)) |
| 190 | (cl-defmethod loadhist-unload-element ((x (head autoload))) | 170 | (hist (get fun 'function-history))) |
| 191 | (loadhist--unload-function x)) | 171 | (cond |
| 172 | ((null hist) | ||
| 173 | (defalias fun nil) | ||
| 174 | ;; Override the change that `defalias' just recorded. | ||
| 175 | (put fun 'function-history nil)) | ||
| 176 | ((equal (car hist) loadhist-unload-filename) | ||
| 177 | (defalias fun (cadr hist)) | ||
| 178 | ;; Set the history afterwards, to override the change that | ||
| 179 | ;; `defalias' records otherwise. | ||
| 180 | (put fun 'function-history (cddr hist))) | ||
| 181 | (t | ||
| 182 | ;; Unloading a file whose definition is "inactive" (i.e. has been | ||
| 183 | ;; overridden by another file): just remove it from the history, | ||
| 184 | ;; so future unloading of that other file has a chance to DTRT. | ||
| 185 | (let* ((tmp (plist-member hist loadhist-unload-filename)) | ||
| 186 | (pos (- (length hist) (length tmp)))) | ||
| 187 | (cl-assert (> pos 1)) | ||
| 188 | (setcdr (nthcdr (- pos 2) hist) (cdr tmp))))))) | ||
| 192 | 189 | ||
| 193 | (cl-defmethod loadhist-unload-element ((_ (head require))) nil) | 190 | (cl-defmethod loadhist-unload-element ((_ (head require))) nil) |
| 194 | (cl-defmethod loadhist-unload-element ((_ (head defface))) nil) | 191 | (cl-defmethod loadhist-unload-element ((_ (head defface))) nil) |
| @@ -257,6 +254,7 @@ something strange, such as redefining an Emacs function." | |||
| 257 | (prin1-to-string dependents) file)))) | 254 | (prin1-to-string dependents) file)))) |
| 258 | (let* ((unload-function-defs-list (feature-symbols feature)) | 255 | (let* ((unload-function-defs-list (feature-symbols feature)) |
| 259 | (file (pop unload-function-defs-list)) | 256 | (file (pop unload-function-defs-list)) |
| 257 | (loadhist-unload-filename file) | ||
| 260 | (name (symbol-name feature)) | 258 | (name (symbol-name feature)) |
| 261 | (unload-hook (intern-soft (concat name "-unload-hook"))) | 259 | (unload-hook (intern-soft (concat name "-unload-hook"))) |
| 262 | (unload-func (intern-soft (concat name "-unload-function")))) | 260 | (unload-func (intern-soft (concat name "-unload-function")))) |
diff --git a/src/data.c b/src/data.c index a5a76a27554..95d29ac9e98 100644 --- a/src/data.c +++ b/src/data.c | |||
| @@ -859,6 +859,43 @@ DEFUN ("fset", Ffset, Sfset, 2, 2, 0, | |||
| 859 | return definition; | 859 | return definition; |
| 860 | } | 860 | } |
| 861 | 861 | ||
| 862 | static void | ||
| 863 | add_to_function_history (Lisp_Object symbol, Lisp_Object olddef) | ||
| 864 | { | ||
| 865 | eassert (!NILP (olddef)); | ||
| 866 | |||
| 867 | Lisp_Object past = Fget (symbol, Qfunction_history); | ||
| 868 | Lisp_Object file = Qnil; | ||
| 869 | /* FIXME: Sadly, `Vload_file_name` gives less precise information | ||
| 870 | (it's sometimes non-nil when it shoujld be nil). */ | ||
| 871 | Lisp_Object tail = Vcurrent_load_list; | ||
| 872 | FOR_EACH_TAIL_SAFE (tail) | ||
| 873 | if (NILP (XCDR (tail)) && STRINGP (XCAR (tail))) | ||
| 874 | file = XCAR (tail); | ||
| 875 | |||
| 876 | Lisp_Object tem = Fplist_member (past, file); | ||
| 877 | if (!NILP (tem)) | ||
| 878 | { /* New def from a file used before. | ||
| 879 | Overwrite the previous record associated with this file. */ | ||
| 880 | if (EQ (tem, past)) | ||
| 881 | /* The new def is from the same file as the last change, so | ||
| 882 | there's nothing to do: unloading the file should revert to | ||
| 883 | the status before the last change rather than before this load. */ | ||
| 884 | return; | ||
| 885 | Lisp_Object pastlen = Flength (past); | ||
| 886 | Lisp_Object temlen = Flength (tem); | ||
| 887 | EMACS_INT tempos = XFIXNUM (pastlen) - XFIXNUM (temlen); | ||
| 888 | eassert (tempos > 1); | ||
| 889 | Lisp_Object prev = Fnthcdr (make_fixnum (tempos - 2), past); | ||
| 890 | /* Remove the previous info for this file. | ||
| 891 | E.g. change `hist` from (... OTHERFILE DEF3 THISFILE DEF2 ...) | ||
| 892 | to (... OTHERFILE DEF2). */ | ||
| 893 | XSETCDR (prev, XCDR (tem)); | ||
| 894 | } | ||
| 895 | /* Push new def from new file. */ | ||
| 896 | Fput (symbol, Qfunction_history, Fcons (file, Fcons (olddef, past))); | ||
| 897 | } | ||
| 898 | |||
| 862 | void | 899 | void |
| 863 | defalias (Lisp_Object symbol, Lisp_Object definition) | 900 | defalias (Lisp_Object symbol, Lisp_Object definition) |
| 864 | { | 901 | { |
| @@ -866,19 +903,19 @@ defalias (Lisp_Object symbol, Lisp_Object definition) | |||
| 866 | bool autoload = AUTOLOADP (definition); | 903 | bool autoload = AUTOLOADP (definition); |
| 867 | if (!will_dump_p () || !autoload) | 904 | if (!will_dump_p () || !autoload) |
| 868 | { /* Only add autoload entries after dumping, because the ones before are | 905 | { /* Only add autoload entries after dumping, because the ones before are |
| 869 | not useful and else we get loads of them from the loaddefs.el. */ | 906 | not useful and else we get loads of them from the loaddefs.el. |
| 870 | Lisp_Object function = XSYMBOL (symbol)->u.s.function; | 907 | That saves us about 110KB in the pdmp file (Jan 2022). */ |
| 871 | 908 | LOADHIST_ATTACH (Fcons (Qdefun, symbol)); | |
| 872 | if (AUTOLOADP (function)) | 909 | } |
| 873 | /* Remember that the function was already an autoload. */ | 910 | } |
| 874 | LOADHIST_ATTACH (Fcons (Qt, symbol)); | ||
| 875 | LOADHIST_ATTACH (Fcons (autoload ? Qautoload : Qdefun, symbol)); | ||
| 876 | |||
| 877 | if (!NILP (Vautoload_queue) && !NILP (function)) | ||
| 878 | Vautoload_queue = Fcons (Fcons (symbol, function), Vautoload_queue); | ||
| 879 | 911 | ||
| 880 | if (AUTOLOADP (function)) | 912 | { |
| 881 | Fput (symbol, Qautoload, XCDR (function)); | 913 | Lisp_Object olddef = XSYMBOL (symbol)->u.s.function; |
| 914 | if (!NILP (olddef)) | ||
| 915 | { | ||
| 916 | if (!NILP (Vautoload_queue)) | ||
| 917 | Vautoload_queue = Fcons (symbol, Vautoload_queue); | ||
| 918 | add_to_function_history (symbol, olddef); | ||
| 882 | } | 919 | } |
| 883 | } | 920 | } |
| 884 | 921 | ||
| @@ -4171,6 +4208,7 @@ syms_of_data (void) | |||
| 4171 | 4208 | ||
| 4172 | DEFSYM (Qinteractive_form, "interactive-form"); | 4209 | DEFSYM (Qinteractive_form, "interactive-form"); |
| 4173 | DEFSYM (Qdefalias_fset_function, "defalias-fset-function"); | 4210 | DEFSYM (Qdefalias_fset_function, "defalias-fset-function"); |
| 4211 | DEFSYM (Qfunction_history, "function-history"); | ||
| 4174 | 4212 | ||
| 4175 | DEFSYM (Qbyte_code_function_p, "byte-code-function-p"); | 4213 | DEFSYM (Qbyte_code_function_p, "byte-code-function-p"); |
| 4176 | 4214 | ||
diff --git a/src/eval.c b/src/eval.c index b083a00a791..1076985d097 100644 --- a/src/eval.c +++ b/src/eval.c | |||
| @@ -2250,21 +2250,17 @@ this does nothing and returns nil. */) | |||
| 2250 | static void | 2250 | static void |
| 2251 | un_autoload (Lisp_Object oldqueue) | 2251 | un_autoload (Lisp_Object oldqueue) |
| 2252 | { | 2252 | { |
| 2253 | Lisp_Object queue, first, second; | ||
| 2254 | |||
| 2255 | /* Queue to unwind is current value of Vautoload_queue. | 2253 | /* Queue to unwind is current value of Vautoload_queue. |
| 2256 | oldqueue is the shadowed value to leave in Vautoload_queue. */ | 2254 | oldqueue is the shadowed value to leave in Vautoload_queue. */ |
| 2257 | queue = Vautoload_queue; | 2255 | Lisp_Object queue = Vautoload_queue; |
| 2258 | Vautoload_queue = oldqueue; | 2256 | Vautoload_queue = oldqueue; |
| 2259 | while (CONSP (queue)) | 2257 | while (CONSP (queue)) |
| 2260 | { | 2258 | { |
| 2261 | first = XCAR (queue); | 2259 | Lisp_Object first = XCAR (queue); |
| 2262 | second = Fcdr (first); | 2260 | if (CONSP (first) && EQ (XCAR (first), make_fixnum (0))) |
| 2263 | first = Fcar (first); | 2261 | Vfeatures = XCDR (first); |
| 2264 | if (EQ (first, make_fixnum (0))) | ||
| 2265 | Vfeatures = second; | ||
| 2266 | else | 2262 | else |
| 2267 | Ffset (first, second); | 2263 | Ffset (first, Fcar (Fcdr (Fget (first, Qfunction_history)))); |
| 2268 | queue = XCDR (queue); | 2264 | queue = XCDR (queue); |
| 2269 | } | 2265 | } |
| 2270 | } | 2266 | } |
diff --git a/src/lread.c b/src/lread.c index 9910db27de7..713c03243cb 100644 --- a/src/lread.c +++ b/src/lread.c | |||
| @@ -5240,12 +5240,9 @@ for symbols and features not associated with any file. | |||
| 5240 | The remaining ENTRIES in the alist element describe the functions and | 5240 | The remaining ENTRIES in the alist element describe the functions and |
| 5241 | variables defined in that file, the features provided, and the | 5241 | variables defined in that file, the features provided, and the |
| 5242 | features required. Each entry has the form `(provide . FEATURE)', | 5242 | features required. Each entry has the form `(provide . FEATURE)', |
| 5243 | `(require . FEATURE)', `(defun . FUNCTION)', `(autoload . SYMBOL)', | 5243 | `(require . FEATURE)', `(defun . FUNCTION)', `(defface . SYMBOL)', |
| 5244 | `(defface . SYMBOL)', `(define-type . SYMBOL)', | 5244 | `(define-type . SYMBOL)', or `(cl-defmethod METHOD SPECIALIZERS)'. |
| 5245 | `(cl-defmethod METHOD SPECIALIZERS)', or `(t . SYMBOL)'. | 5245 | In addition, entries may also be single symbols, |
| 5246 | Entries like `(t . SYMBOL)' may precede a `(defun . FUNCTION)' entry, | ||
| 5247 | and mean that SYMBOL was an autoload before this file redefined it | ||
| 5248 | as a function. In addition, entries may also be single symbols, | ||
| 5249 | which means that symbol was defined by `defvar' or `defconst'. | 5246 | which means that symbol was defined by `defvar' or `defconst'. |
| 5250 | 5247 | ||
| 5251 | During preloading, the file name recorded is relative to the main Lisp | 5248 | During preloading, the file name recorded is relative to the main Lisp |
diff --git a/test/lisp/loadhist-resources/loadhist--bar.el b/test/lisp/loadhist-resources/loadhist--bar.el new file mode 100644 index 00000000000..5c8914ed573 --- /dev/null +++ b/test/lisp/loadhist-resources/loadhist--bar.el | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | ;;; loadhist--bar.el --- Dummy package for loadhist-tests -*- lexical-binding: t; -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2022 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Stefan Monnier <monnier@iro.umontreal.ca> | ||
| 6 | |||
| 7 | ;; This program is free software; you can redistribute it and/or modify | ||
| 8 | ;; it under the terms of the GNU General Public License as published by | ||
| 9 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 10 | ;; (at your option) any later version. | ||
| 11 | |||
| 12 | ;; This program is distributed in the hope that it will be useful, | ||
| 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | ;; GNU General Public License for more details. | ||
| 16 | |||
| 17 | ;; You should have received a copy of the GNU General Public License | ||
| 18 | ;; along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
| 19 | |||
| 20 | ;;; Code: | ||
| 21 | |||
| 22 | (autoload 'loadhist--foo-inc "loadhist--foo") | ||
| 23 | |||
| 24 | (defun loadhist--bar-dec (x) (1- x)) | ||
| 25 | |||
| 26 | (provide 'loadhist--bar) | ||
| 27 | ;;; loadhist--bar.el ends here | ||
diff --git a/test/lisp/loadhist-resources/loadhist--foo.el b/test/lisp/loadhist-resources/loadhist--foo.el new file mode 100644 index 00000000000..3574c220135 --- /dev/null +++ b/test/lisp/loadhist-resources/loadhist--foo.el | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | ;;; loadhist--foo.el --- Dummy package for loadhist-tests -*- lexical-binding: t; -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2022 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: Stefan Monnier <monnier@iro.umontreal.ca> | ||
| 6 | |||
| 7 | ;; This program is free software; you can redistribute it and/or modify | ||
| 8 | ;; it under the terms of the GNU General Public License as published by | ||
| 9 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 10 | ;; (at your option) any later version. | ||
| 11 | |||
| 12 | ;; This program is distributed in the hope that it will be useful, | ||
| 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | ;; GNU General Public License for more details. | ||
| 16 | |||
| 17 | ;; You should have received a copy of the GNU General Public License | ||
| 18 | ;; along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
| 19 | |||
| 20 | ;;; Commentary: | ||
| 21 | |||
| 22 | ;;; Code: | ||
| 23 | |||
| 24 | (autoload 'loadhist--bar-dec "loadhist--bar") | ||
| 25 | |||
| 26 | (defun loadhist--foo-inc (x) (1+ x)) | ||
| 27 | |||
| 28 | (provide 'loadhist--foo) | ||
| 29 | ;;; loadhist--foo.el ends here | ||
diff --git a/test/lisp/loadhist-tests.el b/test/lisp/loadhist-tests.el index a941ac06320..ef5fc164d34 100644 --- a/test/lisp/loadhist-tests.el +++ b/test/lisp/loadhist-tests.el | |||
| @@ -54,4 +54,51 @@ | |||
| 54 | (should-error (unload-feature 'dired)) | 54 | (should-error (unload-feature 'dired)) |
| 55 | (unload-feature 'dired-x)) | 55 | (unload-feature 'dired-x)) |
| 56 | 56 | ||
| 57 | (defvar loadhist--tests-dir (file-name-directory (macroexp-file-name))) | ||
| 58 | |||
| 59 | (ert-deftest loadhist-tests-unload-feature-nested () | ||
| 60 | (add-to-list 'load-path (expand-file-name | ||
| 61 | "loadhist-resources/" | ||
| 62 | loadhist--tests-dir)) | ||
| 63 | (declare-function loadhist--foo-inc "loadhist--foo") | ||
| 64 | (declare-function loadhist--bar-dec "loadhist--dec") | ||
| 65 | (load "loadhist--foo" nil t) | ||
| 66 | (should (and (functionp 'loadhist--bar-dec) (functionp 'loadhist--foo-inc))) | ||
| 67 | (should (autoloadp (symbol-function 'loadhist--bar-dec))) | ||
| 68 | (load "loadhist--bar" nil t) | ||
| 69 | (should (and (functionp 'loadhist--bar-dec) (functionp 'loadhist--foo-inc))) | ||
| 70 | (should (not (autoloadp (symbol-function 'loadhist--bar-dec)))) | ||
| 71 | (should (not (autoloadp (symbol-function 'loadhist--foo-inc)))) | ||
| 72 | (should (equal (list 40 42) | ||
| 73 | (list (loadhist--bar-dec 41) (loadhist--foo-inc 41)))) | ||
| 74 | (unload-feature 'loadhist--bar) | ||
| 75 | (should (and (functionp 'loadhist--bar-dec) (functionp 'loadhist--foo-inc))) | ||
| 76 | (should (autoloadp (symbol-function 'loadhist--bar-dec))) | ||
| 77 | (should (not (autoloadp (symbol-function 'loadhist--foo-inc)))) | ||
| 78 | (unload-feature 'loadhist--foo) | ||
| 79 | (should (null (symbol-function 'loadhist--bar-dec))) | ||
| 80 | (should (null (symbol-function 'loadhist--foo-inc))) | ||
| 81 | (should (null (get 'loadhist--bar-dec 'function-history))) | ||
| 82 | (should (null (get 'loadhist--foo-inc 'function-history)))) | ||
| 83 | |||
| 84 | (ert-deftest loadhist-tests-unload-feature-notnested () | ||
| 85 | (add-to-list 'load-path (expand-file-name | ||
| 86 | "loadhist-resources/" | ||
| 87 | loadhist--tests-dir)) | ||
| 88 | (load "loadhist--foo" nil t) | ||
| 89 | (load "loadhist--bar" nil t) | ||
| 90 | (should (equal (list 40 42) | ||
| 91 | (list (loadhist--bar-dec 41) (loadhist--foo-inc 41)))) | ||
| 92 | (unload-feature 'loadhist--foo) | ||
| 93 | (should (functionp 'loadhist--bar-dec)) | ||
| 94 | (should (not (autoloadp (symbol-function 'loadhist--bar-dec)))) | ||
| 95 | (should (let ((f (symbol-function 'loadhist--foo-inc))) | ||
| 96 | ;; Both choices seem acceptable. | ||
| 97 | (or (null f) (autoloadp f)))) | ||
| 98 | (unload-feature 'loadhist--bar) | ||
| 99 | (should (null (symbol-function 'loadhist--bar-dec))) | ||
| 100 | (should (null (symbol-function 'loadhist--foo-inc))) | ||
| 101 | (should (null (get 'loadhist--bar-dec 'function-history))) | ||
| 102 | (should (null (get 'loadhist--foo-inc 'function-history)))) | ||
| 103 | |||
| 57 | ;;; loadhist-tests.el ends here | 104 | ;;; loadhist-tests.el ends here |