# HG changeset patch # User Richard M. Stallman # Date 788248685 0 # Node ID ff7189e5e459b6b3c883287d16916c9c06a4307b # Parent 170c4c188d4f48889fae17b51f6de40207ae185c (byte-compile-dest-file): New variable. (byte-compile-file): Bind that var, early on. (byte-compile-dynamic): New variable. (byte-compile-dynamic-docstrings): New variable. (byte-compile-close-variables): Bind byte-compile-dynamic, byte-compile-dynamic-docstrings, and byte-compiler-compatibility. (byte-compile-file): Call normal-mode, not set-auto-mode. (byte-compile-output-docform): New arguments PREFACE, NAME, SPECINDEX, QUOTED. Callers changed. Output doc strings as references to the .elc file itself, using #@ and #$ constructs. (byte-compile-output-as-comment): New function. (byte-compile-insert-header): Don't save-excursion. Insert at point, and move point. Insert extra newline at end. (byte-compile-from-buffer): Insert the header before compilation. diff -r 170c4c188d4f -r ff7189e5e459 lisp/emacs-lisp/bytecomp.el --- a/lisp/emacs-lisp/bytecomp.el Sat Dec 24 04:27:01 1994 +0000 +++ b/lisp/emacs-lisp/bytecomp.el Sat Dec 24 05:58:05 1994 +0000 @@ -246,6 +246,29 @@ "*If non-nil, the optimizer may delete forms that may signal an error. This includes variable references and calls to functions such as `car'.") +(defvar byte-compile-dynamic nil + "*If non-nil, compile function bodies so they load lazily. +They are hidden comments in the compiled file, and brought into core when the +function is called. + +To enable this option, make it a file-local variable +in the source file you want it to apply to. +For example, add -*-byte-compile-dynamic: t;-*- on the first line. + +When this option is true, if you load the compiled file and then move it, +the functions you loaded will not be able to run.") + +(defvar byte-compile-dynamic-docstrings t + "*If non-nil, compile doc strings for lazy access. +We bury the doc strings of functions and variables +inside comments in the file, and bring them into core only when they +are actually needed. + +When this option is true, if you load the compiled file and then move it, +you won't be able to find the documentation of anything in that file. + +This option is enabled by default because it reduces Emacs memory usage.") + (defvar byte-optimize-log nil "*If true, the byte-compiler will log its optimizations into *Compile-Log*. If this is 'source, then only source-level optimizations will be logged. @@ -677,8 +700,9 @@ ;;; byte compiler messages -(defconst byte-compile-current-form nil) -(defconst byte-compile-current-file nil) +(defvar byte-compile-current-form nil) +(defvar byte-compile-current-file nil) +(defvar byte-compile-dest-file nil) (defmacro byte-compile-log (format-string &rest args) (list 'and @@ -899,7 +923,7 @@ (sig (and def (byte-compile-arglist-signature (if (eq 'lambda (car-safe def)) (nth 1 def) - (if (compiled-function-p def) + (if (byte-code-function-p def) (aref def 0) '(&rest def)))))) (ncall (length (cdr form)))) @@ -934,7 +958,7 @@ (let ((sig1 (byte-compile-arglist-signature (if (eq 'lambda (car-safe old)) (nth 1 old) - (if (compiled-function-p old) + (if (byte-code-function-p old) (aref old 0) '(&rest def))))) (sig2 (byte-compile-arglist-signature (nth 2 form)))) @@ -1019,6 +1043,10 @@ ;; (byte-compile-verbose byte-compile-verbose) (byte-optimize byte-optimize) + (byte-compile-compatibility byte-compile-compatibility) + (byte-compile-dynamic byte-compile-dynamic) + (byte-compile-dynamic-docstrings + byte-compile-dynamic-docstrings) ;; (byte-compile-generate-emacs19-bytecodes ;; byte-compile-generate-emacs19-bytecodes) (byte-compile-warnings (if (eq byte-compile-warnings t) @@ -1150,7 +1178,10 @@ (if byte-compile-verbose (message "Compiling %s..." filename)) (let ((byte-compile-current-file filename) - target-file input-buffer output-buffer) + target-file input-buffer output-buffer + byte-compile-dest-file) + (setq target-file (byte-compile-dest-file filename)) + (setq byte-compile-dest-file target-file) (save-excursion (setq input-buffer (get-buffer-create " *Compiler Input*")) (set-buffer input-buffer) @@ -1158,8 +1189,9 @@ (insert-file-contents filename) ;; Run hooks including the uncompression hook. ;; If they change the file name, then change it for the output also. - (let ((buffer-file-name filename)) - (set-auto-mode) + (let ((buffer-file-name filename) + (enable-local-eval nil)) + (normal-mode) (setq filename buffer-file-name))) (setq byte-compiler-error-flag nil) ;; It is important that input-buffer not be current at this call, @@ -1174,11 +1206,6 @@ (goto-char (point-max)) (insert "\n") ; aaah, unix. (let ((vms-stmlf-recfm t)) - (setq target-file (byte-compile-dest-file filename)) -;;; (or byte-compile-overwrite-file -;;; (condition-case () -;;; (delete-file target-file) -;;; (error nil))) (if (file-writable-p target-file) (let ((kanji-flag nil)) ; for nemacs, from Nakagawa Takayuki (if (or (eq system-type 'ms-dos) (eq system-type 'windows-nt)) @@ -1191,12 +1218,7 @@ (if (file-exists-p target-file) "cannot overwrite file" "directory not writable or nonexistent") - target-file))) -;;; (or byte-compile-overwrite-file -;;; (condition-case () -;;; (set-file-modes target-file (file-modes filename)) -;;; (error nil))) - ) + target-file)))) (kill-buffer (current-buffer))) (if (and byte-compile-generate-call-tree (or (eq t byte-compile-generate-call-tree) @@ -1252,115 +1274,104 @@ (defun byte-compile-from-buffer (inbuffer &optional filename) ;; Filename is used for the loading-into-Emacs-18 error message. - (let (outbuffer) - (let (;; Prevent truncation of flonums and lists as we read and print them - (float-output-format nil) - (case-fold-search nil) - (print-length nil) - ;; Simulate entry to byte-compile-top-level - (byte-compile-constants nil) - (byte-compile-variables nil) - (byte-compile-tag-number 0) - (byte-compile-depth 0) - (byte-compile-maxdepth 0) - (byte-compile-output nil) - ;; #### This is bound in b-c-close-variables. - ;; (byte-compile-warnings (if (eq byte-compile-warnings t) - ;; byte-compile-warning-types - ;; byte-compile-warnings)) - ) - (byte-compile-close-variables - (save-excursion - (setq outbuffer - (set-buffer (get-buffer-create " *Compiler Output*"))) - (erase-buffer) - ;; (emacs-lisp-mode) - (setq case-fold-search nil) + (let (outbuffer + ;; Prevent truncation of flonums and lists as we read and print them + (float-output-format nil) + (case-fold-search nil) + (print-length nil) + ;; Simulate entry to byte-compile-top-level + (byte-compile-constants nil) + (byte-compile-variables nil) + (byte-compile-tag-number 0) + (byte-compile-depth 0) + (byte-compile-maxdepth 0) + (byte-compile-output nil) + ;; #### This is bound in b-c-close-variables. + ;; (byte-compile-warnings (if (eq byte-compile-warnings t) + ;; byte-compile-warning-types + ;; byte-compile-warnings)) + ) + (byte-compile-close-variables + (save-excursion + (setq outbuffer + (set-buffer (get-buffer-create " *Compiler Output*"))) + (erase-buffer) + ;; (emacs-lisp-mode) + (setq case-fold-search nil) + (and filename (byte-compile-insert-header filename)) - ;; This is a kludge. Some operating systems (OS/2, DOS) need to - ;; write files containing binary information specially. - ;; Under most circumstances, such files will be in binary - ;; overwrite mode, so those OS's use that flag to guess how - ;; they should write their data. Advise them that .elc files - ;; need to be written carefully. - (setq overwrite-mode 'overwrite-mode-binary)) - (displaying-byte-compile-warnings - (save-excursion - (set-buffer inbuffer) - (goto-char 1) - (while (progn - (while (progn (skip-chars-forward " \t\n\^l") - (looking-at ";")) - (forward-line 1)) - (not (eobp))) - (byte-compile-file-form (read inbuffer))) - ;; Compile pending forms at end of file. - (byte-compile-flush-pending) - (and filename (byte-compile-insert-header filename)) - (byte-compile-warn-about-unresolved-functions) - ;; always do this? When calling multiple files, it - ;; would be useful to delay this warning until all have - ;; been compiled. - (setq byte-compile-unresolved-functions nil))) - (save-excursion - (set-buffer outbuffer) - (goto-char (point-min))))) + ;; This is a kludge. Some operating systems (OS/2, DOS) need to + ;; write files containing binary information specially. + ;; Under most circumstances, such files will be in binary + ;; overwrite mode, so those OS's use that flag to guess how + ;; they should write their data. Advise them that .elc files + ;; need to be written carefully. + (setq overwrite-mode 'overwrite-mode-binary)) + (displaying-byte-compile-warnings + (save-excursion + (set-buffer inbuffer) + (goto-char 1) + + ;; Compile the forms from the input buffer. + (while (progn + (while (progn (skip-chars-forward " \t\n\^l") + (looking-at ";")) + (forward-line 1)) + (not (eobp))) + (byte-compile-file-form (read inbuffer))) + + ;; Compile pending forms at end of file. + (byte-compile-flush-pending) + (byte-compile-warn-about-unresolved-functions) + ;; SHould we always do this? When calling multiple files, it + ;; would be useful to delay this warning until all have + ;; been compiled. + (setq byte-compile-unresolved-functions nil)))) outbuffer)) -;;; (if (not eval) -;;; outbuffer -;;; (while (condition-case nil -;;; (progn (setq form (read outbuffer)) -;;; t) -;;; (end-of-file nil)) -;;; (eval form)) -;;; (kill-buffer outbuffer) -;;; nil)))) (defun byte-compile-insert-header (filename) - (save-excursion - (set-buffer outbuffer) - (goto-char 1) - ;; - ;; The magic number of .elc files is ";ELC", or 0x3B454C43. After that is - ;; the file-format version number (18 or 19) as a byte, followed by some - ;; nulls. The primary motivation for doing this is to get some binary - ;; characters up in the first line of the file so that `diff' will simply - ;; say "Binary files differ" instead of actually doing a diff of two .elc - ;; files. An extra benefit is that you can add this to /etc/magic: - ;; - ;; 0 string ;ELC GNU Emacs Lisp compiled file, - ;; >4 byte x version %d - ;; - (insert - ";ELC" - (if (byte-compile-version-cond byte-compile-compatibility) 18 19) - "\000\000\000\n" - ) - (insert ";;; compiled by " user-mail-address " on " - (current-time-string) "\n;;; from file " filename "\n") - (insert ";;; emacs version " emacs-version ".\n") - (insert ";;; bytecomp version " byte-compile-version "\n;;; " - (cond - ((eq byte-optimize 'source) "source-level optimization only") - ((eq byte-optimize 'byte) "byte-level optimization only") - (byte-optimize "optimization is on") - (t "optimization is off")) - (if (byte-compile-version-cond byte-compile-compatibility) - "; compiled with Emacs 18 compatibility.\n" - ".\n")) - (if (not (byte-compile-version-cond byte-compile-compatibility)) - (insert ";;; this file uses opcodes which do not exist in Emacs 18.\n" - ;; Have to check if emacs-version is bound so that this works - ;; in files loaded early in loadup.el. - "\n(if (and (boundp 'emacs-version)\n" - "\t (or (and (boundp 'epoch::version) epoch::version)\n" - "\t (string-lessp emacs-version \"19\")))\n" - " (error \"`" - ;; This escapes all backslashes in FILENAME. Needed on Windows. - (substring (prin1-to-string filename) 1 -1) - "' was compiled for Emacs 19\"))\n" - )) - )) + (set-buffer outbuffer) + (goto-char 1) + ;; + ;; The magic number of .elc files is ";ELC", or 0x3B454C43. After that is + ;; the file-format version number (18 or 19) as a byte, followed by some + ;; nulls. The primary motivation for doing this is to get some binary + ;; characters up in the first line of the file so that `diff' will simply + ;; say "Binary files differ" instead of actually doing a diff of two .elc + ;; files. An extra benefit is that you can add this to /etc/magic: + ;; + ;; 0 string ;ELC GNU Emacs Lisp compiled file, + ;; >4 byte x version %d + ;; + (insert + ";ELC" + (if (byte-compile-version-cond byte-compile-compatibility) 18 19) + "\000\000\000\n" + ) + (insert ";;; compiled by " user-mail-address " on " + (current-time-string) "\n;;; from file " filename "\n") + (insert ";;; emacs version " emacs-version ".\n") + (insert ";;; bytecomp version " byte-compile-version "\n;;; " + (cond + ((eq byte-optimize 'source) "source-level optimization only") + ((eq byte-optimize 'byte) "byte-level optimization only") + (byte-optimize "optimization is on") + (t "optimization is off")) + (if (byte-compile-version-cond byte-compile-compatibility) + "; compiled with Emacs 18 compatibility.\n" + ".\n")) + (if (not (byte-compile-version-cond byte-compile-compatibility)) + (insert ";;; this file uses opcodes which do not exist in Emacs 18.\n" + ;; Have to check if emacs-version is bound so that this works + ;; in files loaded early in loadup.el. + "\n(if (and (boundp 'emacs-version)\n" + "\t (or (and (boundp 'epoch::version) epoch::version)\n" + "\t (string-lessp emacs-version \"19\")))\n" + " (error \"`" + ;; This escapes all backslashes in FILENAME. Needed on Windows. + (substring (prin1-to-string filename) 1 -1) + "' was compiled for Emacs 19\"))\n\n" + ))) (defun byte-compile-output-file-form (form) @@ -1372,7 +1383,8 @@ ;; it here. (if (and (memq (car-safe form) '(defun defmacro defvar defconst autoload)) (stringp (nth 3 form))) - (byte-compile-output-docform '("\n(" 3 ")") form) + (byte-compile-output-docform nil nil '("\n(" 3 ")") form nil + (eq (car form) 'autoload)) (let ((print-escape-newlines t) (print-readably t) ; print #[] for bytecode, 'x for (quote x) (print-gensym nil)) ; this is too dangerous for now @@ -1380,27 +1392,67 @@ (prin1 form outbuffer) nil))) -(defun byte-compile-output-docform (info form) +(defun byte-compile-output-docform (preface name info form specindex quoted) ;; Print a form with a doc string. INFO is (prefix doc-index postfix). + ;; If PREFACE and NAME are non-nil, print them too, + ;; before INFO and the FORM but after the doc string itself. + ;; If SPECINDEX is non-nil, it is the index in FORM + ;; of the function bytecode string. In that case, + ;; we output that argument and the following argument (the constants vector) + ;; together, for lazy loading. + ;; QUOTED says that we have to put a quote before the + ;; list that represents a doc string reference. + ;; `autoload' needs that. (set-buffer (prog1 (current-buffer) (set-buffer outbuffer) - (insert (car info)) - (let ((docl (nthcdr (nth 1 info) form)) - (print-escape-newlines t) - (print-readably t) ; print #[] for bytecode, 'x for (quote x) - (print-gensym nil)) ; this is too dangerous for now - (prin1 (car form) outbuffer) - (while (setq form (cdr form)) - (insert " ") - (if (eq form docl) - (let ((print-escape-newlines nil)) - (goto-char (prog1 (1+ (point)) - (prin1 (car form) outbuffer))) - (insert "\\\n") - (goto-char (point-max))) - (prin1 (car form) outbuffer)))) - (insert (nth 2 info)))) + (let (position) + + ;; Insert the doc string, and make it a comment with #@LENGTH. + (and (>= (nth 1 info) 0) + byte-compile-dynamic-docstrings + (progn + ;; Make the doc string start at beginning of line + ;; for make-docfile's sake. + (insert "\n") + (setq position + (byte-compile-output-as-comment + (nth (nth 1 info) form) nil)))) + + (if preface + (progn + (insert preface) + (prin1 name outbuffer))) + (insert (car info)) + (let ((print-escape-newlines t) + (print-readably t) ; print #[] for bytecode, 'x for (quote x) + (print-gensym nil) ; this is too dangerous for now + (index 0)) + (prin1 (car form) outbuffer) + (while (setq form (cdr form)) + (setq index (1+ index)) + (insert " ") + (cond ((and (numberp specindex) (= index specindex)) + (let ((position + (byte-compile-output-as-comment + (cons (car form) (nth 1 form)) + t))) + (princ (format "(#$ . %d) nil" position) outbuffer) + (setq form (cdr form)) + (setq index (1+ index)))) + ((= index (nth 1 info)) + (if position + (princ (format (if quoted "'(#$ . %d)" "(#$ . %d)") + position) + outbuffer) + (let ((print-escape-newlines nil)) + (goto-char (prog1 (1+ (point)) + (prin1 (car form) outbuffer))) + (insert "\\\n") + (goto-char (point-max))))) + (t + (prin1 (car form) outbuffer))))) + (insert (nth 2 info))))) nil) (defun byte-compile-keep-pending (form &optional handler) @@ -1591,36 +1643,82 @@ (eq 'lambda (car-safe (nth 1 code)))) (cons (car form) (cons name (cdr (nth 1 code)))) + (byte-compile-flush-pending) (if (not (stringp (nth 3 form))) - ;; No doc string to make-docfile; insert form in normal code. - (byte-compile-keep-pending - (list (if (byte-compile-version-cond byte-compile-compatibility) - 'fset 'defalias) - (list 'quote name) - (cond ((not macrop) - code) - ((eq 'make-byte-code (car-safe code)) - (list 'cons ''macro code)) - ((list 'quote (if macrop - (cons 'macro new-one) - new-one)))))) + ;; No doc string. Provide -1 as the "doc string index" + ;; so that no element will be treated as a doc string. + (byte-compile-output-docform + (if (byte-compile-version-cond byte-compile-compatibility) + "\n(fset '" "\n(defalias '") + name + (cond ((atom code) + (if macrop '(" '(macro . #[" -1 "])") '(" #[" -1 "]"))) + ((eq (car code) 'quote) + (setq code new-one) + (if macrop '(" '(macro " -1 ")") '(" '(" -1 ")"))) + ((if macrop '(" (cons 'macro (" -1 "))") '(" (" -1 ")")))) + (append code nil) + (and (atom code) byte-compile-dynamic + 1) + nil) ;; Output the form by hand, that's much simpler than having ;; b-c-output-file-form analyze the defalias. - (byte-compile-flush-pending) - (princ (if (byte-compile-version-cond byte-compile-compatibility) - "\n(fset '" "\n(defalias '") - outbuffer) - (prin1 name outbuffer) (byte-compile-output-docform + (if (byte-compile-version-cond byte-compile-compatibility) + "\n(fset '" "\n(defalias '") + name (cond ((atom code) (if macrop '(" '(macro . #[" 4 "])") '(" #[" 4 "]"))) ((eq (car code) 'quote) (setq code new-one) (if macrop '(" '(macro " 2 ")") '(" '(" 2 ")"))) ((if macrop '(" (cons 'macro (" 5 "))") '(" (" 5 ")")))) - (append code nil)) - (princ ")" outbuffer) - nil))))) + (append code nil) + (and (atom code) byte-compile-dynamic + 1) + nil)) + (princ ")" outbuffer) + nil)))) + +;; Print Lisp object EXP in the output file, inside a comment, +;; and return the file position it will have. +;; If QUOTED is non-nil, print with quoting; otherwise, print without quoting. +(defun byte-compile-output-as-comment (exp quoted) + (let ((position (point))) + (set-buffer + (prog1 (current-buffer) + (set-buffer outbuffer) + + ;; Insert EXP, and make it a comment with #@LENGTH. + (insert " ") + (if quoted + (prin1 exp outbuffer) + (princ exp outbuffer)) + (goto-char position) + ;; Quote certain special characters as needed. + ;; get_doc_string in doc.c does the unquoting. + (while (search-forward "\^A" nil t) + (replace-match "\^A\^A" t t)) + (goto-char position) + (while (search-forward "\000" nil t) + (replace-match "\^A0" t t)) + (goto-char position) + (while (search-forward "\037" nil t) + (replace-match "\^A_" t t)) + (goto-char (point-max)) + (insert "\037") + (goto-char position) + (insert "#@" (format "%d" (- (point-max) position))) + + ;; Save the file position of the object. + ;; Note we should add 1 to skip the space + ;; that we inserted before the actual doc string, + ;; and subtract 1 to convert from an 1-origin Emacs position + ;; to a file position; they cancel. + (setq position (point)) + (goto-char (point-max)))) + position)) + ;;;###autoload