Teach 'symbol-file' about .eln natively-compiled files

* lisp/subr.el (locate-eln-file): New function.
(symbol-file): Accept an optional 3rd argument NATIVE-P, and, if
non-nil, try to locate and report the .eln file where SYMBOL was
defined.

* etc/NEWS:
* doc/lispref/loading.texi (Where Defined): Document the new
optional argument of 'symbol-file'.
This commit is contained in:
Eli Zaretskii 2022-08-03 17:16:09 +03:00
parent 6861ed1175
commit ec22e923c0
3 changed files with 96 additions and 22 deletions

View file

@ -1032,7 +1032,7 @@ with a call to @code{provide}. The order of the elements in the
@cindex symbol, where defined
@cindex where was a symbol defined
@defun symbol-file symbol &optional type
@defun symbol-file symbol &optional type native-p
This function returns the name of the file that defined @var{symbol}.
If @var{type} is @code{nil}, then any kind of definition is acceptable.
If @var{type} is @code{defun}, @code{defvar}, or @code{defface}, that
@ -1043,6 +1043,14 @@ The value is normally an absolute file name. It can also be @code{nil},
if the definition is not associated with any file. If @var{symbol}
specifies an autoloaded function, the value can be a relative file name
without extension.
If the optional third argument @var{native-p} is non-@code{nil}, and
Emacs was built with native compilation support (@pxref{Native
Compilation}), this function will try to find the @file{.eln} file
that defined @var{symbol}, instead of the @file{.elc} or @file{.el}
file. If such a @file{.eln} file is found and is not outdated, the
function will return its absolute file name; otherwise it will report
the name of either the source or the byte-compiled file.
@end defun
The basis for @code{symbol-file} is the data in the variable

View file

@ -2575,6 +2575,11 @@ things to be saved.
** New function 'string-equal-ignore-case'.
This compares strings ignoring case differences.
** 'symbol-file' can now report natively-compiled .eln files.
If Emacs was built with native-compilation enabled, Lisp programs can
now call 'symbol-file' with the new optional 3rd argument non-nil to
request the name of the .eln file which defined a given symbol.
** Themes
---

View file

@ -2700,18 +2700,44 @@ This is to `put' what `defalias' is to `fset'."
(setcdr ps (cons symbol (cdr ps))))))
(put symbol prop val))
(defun symbol-file (symbol &optional type)
(defvar comp-native-version-dir)
(defvar native-comp-eln-load-path)
(declare-function subr-native-elisp-p "data.c")
(declare-function native-comp-unit-file "data.c")
(declare-function subr-native-comp-unit "data.c")
(declare-function comp-el-to-eln-rel-filename "comp.c")
(defun locate-eln-file (eln-file)
"Locate a natively-compiled ELN-FILE by searching its load path.
This function looks in directories named by `native-comp-eln-load-path'."
(or (locate-file-internal (concat comp-native-version-dir "/" eln-file)
native-comp-eln-load-path)
(locate-file-internal
;; Preloaded *.eln files live in the preloaded/ subdirectory of
;; the last entry in `native-comp-eln-load-path'.
(concat comp-native-version-dir "/preloaded/" eln-file)
(last native-comp-eln-load-path))))
(defun symbol-file (symbol &optional type native-p)
"Return the name of the file that defined SYMBOL.
The value is normally an absolute file name. It can also be nil,
if the definition is not associated with any file. If SYMBOL
specifies an autoloaded function, the value can be a relative
file name without extension.
If TYPE is nil, then any kind of definition is acceptable. If
TYPE is `defun', `defvar', or `defface', that specifies function
If TYPE is nil, then any kind of SYMBOL's definition is acceptable.
If TYPE is `defun', `defvar', or `defface', that specifies function
definition, variable definition, or face definition only.
Otherwise TYPE is assumed to be a symbol property.
If NATIVE-P is non-nil, and SYMBOL was loaded from a .eln file,
this function will return the absolute file name of that .eln file,
if found. Note that if the .eln file is older than its source .el
file, Emacs won't load such an outdated .eln file, and this function
will not return it. If the .eln file couldn't be found, or is
outdated, the function returns the corresponding .elc or .el file
instead.
This function only works for symbols defined in Lisp files. For
symbols that are defined in C files, use `help-C-file-name'
instead."
@ -2719,24 +2745,59 @@ instead."
(symbolp symbol)
(autoloadp (symbol-function symbol)))
(nth 1 (symbol-function symbol))
(catch 'found
(pcase-dolist (`(,file . ,elems) load-history)
(when (if type
(if (eq type 'defvar)
;; Variables are present just as their names.
(member symbol elems)
;; Many other types are represented as (TYPE . NAME).
(or (member (cons type symbol) elems)
(memq symbol (alist-get type
(alist-get 'define-symbol-props
elems)))))
;; We accept all types, so look for variable def
;; and then for any other kind.
(or (member symbol elems)
(let ((match (rassq symbol elems)))
(and match
(not (eq 'require (car match)))))))
(throw 'found file))))))
(if (and native-p (or (null type) (eq type 'defun))
(symbolp symbol)
(native-comp-available-p)
;; If it's a defun, we have a shortcut.
(subr-native-elisp-p (symbol-function symbol)))
;; native-comp-unit-file returns unnormalized file names.
(expand-file-name (native-comp-unit-file (subr-native-comp-unit
(symbol-function symbol))))
(let ((elc-file
(catch 'found
(pcase-dolist (`(,file . ,elems) load-history)
(when (if type
(if (eq type 'defvar)
;; Variables are present just as their
;; names.
(member symbol elems)
;; Many other types are represented as
;; (TYPE . NAME).
(or (member (cons type symbol) elems)
(memq
symbol
(alist-get type
(alist-get 'define-symbol-props
elems)))))
;; We accept all types, so look for variable def
;; and then for any other kind.
(or (member symbol elems)
(let ((match (rassq symbol elems)))
(and match
(not (eq 'require (car match)))))))
(throw 'found file))))))
;; If they asked for the .eln file, try to find it.
(or (and elc-file
native-p
(native-comp-available-p)
(let* ((sans-ext (file-name-sans-extension elc-file))
(el-file
(and (fboundp 'zlib-available-p)
(zlib-available-p)
(concat sans-ext ".el.gz")))
(el-file-backup (concat sans-ext ".el")))
(or (and el-file (file-exists-p el-file))
(and (file-exists-p el-file-backup)
(setq el-file el-file-backup))
(setq el-file nil))
(when (stringp el-file)
(let ((eln-file (locate-eln-file
(comp-el-to-eln-rel-filename el-file))))
;; Emacs will not load an outdated .eln file,
;; so we mimic this behavior here.
(if (file-newer-than-file-p eln-file el-file)
eln-file)))))
elc-file)))))
(declare-function read-library-name "find-func" nil)