]> git.eshelyaron.com Git - emacs.git/commitdiff
Teach 'symbol-file' about .eln natively-compiled files
authorEli Zaretskii <eliz@gnu.org>
Wed, 3 Aug 2022 14:16:09 +0000 (17:16 +0300)
committerEli Zaretskii <eliz@gnu.org>
Wed, 3 Aug 2022 14:16:09 +0000 (17:16 +0300)
* 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'.

doc/lispref/loading.texi
etc/NEWS
lisp/subr.el

index 54fc16ec9f053cbc513c1b8d9db8109cefc46ed0..e8dce433a5b6a80c6df378578a97b4dd07d8e3ac 100644 (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
index b88fb63662378869b07a9be824072f9687924b1d..7e8ed465eb61c7470079b08a883cbcdf58a87100 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -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
 
 ---
index ff82d0d1d8ddda36ffae1ad3a6c41b60de70612b..1b59db0604ce74a82df0b045e5cabc8564677c61 100644 (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)