mirrored from git://git.sv.gnu.org/emacs.git
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
bytecomp.el
5223 lines (4833 loc) · 213 KB
/
bytecomp.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;; bytecomp.el --- compilation of Lisp code into byte code -*- lexical-binding: t -*-
;; Copyright (C) 1985-1987, 1992, 1994, 1998, 2000-2018 Free Software
;; Foundation, Inc.
;; Author: Jamie Zawinski <jwz@lucid.com>
;; Hallvard Furuseth <hbf@ulrik.uio.no>
;; Maintainer: emacs-devel@gnu.org
;; Keywords: lisp
;; Package: emacs
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; The Emacs Lisp byte compiler. This crunches lisp source into a sort
;; of p-code (`lapcode') which takes up less space and can be interpreted
;; faster. [`LAP' == `Lisp Assembly Program'.]
;; The user entry points are byte-compile-file and byte-recompile-directory.
;;; Todo:
;; - Turn "not bound at runtime" functions into autoloads.
;;; Code:
;; ========================================================================
;; Entry points:
;; byte-recompile-directory, byte-compile-file,
;; byte-recompile-file,
;; batch-byte-compile, batch-byte-recompile-directory,
;; byte-compile, compile-defun,
;; display-call-tree
;; (byte-compile-buffer and byte-compile-and-load-file were turned off
;; because they are not terribly useful and get in the way of completion.)
;; This version of the byte compiler has the following improvements:
;; + optimization of compiled code:
;; - removal of unreachable code;
;; - removal of calls to side-effectless functions whose return-value
;; is unused;
;; - compile-time evaluation of safe constant forms, such as (consp nil)
;; and (ash 1 6);
;; - open-coding of literal lambdas;
;; - peephole optimization of emitted code;
;; - trivial functions are left uncompiled for speed.
;; + support for inline functions;
;; + compile-time evaluation of arbitrary expressions;
;; + compile-time warning messages for:
;; - functions being redefined with incompatible arglists;
;; - functions being redefined as macros, or vice-versa;
;; - functions or macros defined multiple times in the same file;
;; - functions being called with the incorrect number of arguments;
;; - functions being called which are not defined globally, in the
;; file, or as autoloads;
;; - assignment and reference of undeclared free variables;
;; - various syntax errors;
;; + correct compilation of nested defuns, defmacros, defvars and defsubsts;
;; + correct compilation of top-level uses of macros;
;; + the ability to generate a histogram of functions called.
;; User customization variables: M-x customize-group bytecomp
;; New Features:
;;
;; o The form `defsubst' is just like `defun', except that the function
;; generated will be open-coded in compiled code which uses it. This
;; means that no function call will be generated, it will simply be
;; spliced in. Lisp functions calls are very slow, so this can be a
;; big win.
;;
;; You can generally accomplish the same thing with `defmacro', but in
;; that case, the defined procedure can't be used as an argument to
;; mapcar, etc.
;;
;; o You can also open-code one particular call to a function without
;; open-coding all calls. Use the 'inline' form to do this, like so:
;;
;; (inline (foo 1 2 3)) ;; `foo' will be open-coded
;; or...
;; (inline ;; `foo' and `baz' will be
;; (foo 1 2 3 (bar 5)) ;; open-coded, but `bar' will not.
;; (baz 0))
;;
;; o It is possible to open-code a function in the same file it is defined
;; in without having to load that file before compiling it. The
;; byte-compiler has been modified to remember function definitions in
;; the compilation environment in the same way that it remembers macro
;; definitions.
;;
;; o Forms like ((lambda ...) ...) are open-coded.
;;
;; o The form `eval-when-compile' is like progn, except that the body
;; is evaluated at compile-time. When it appears at top-level, this
;; is analogous to the Common Lisp idiom (eval-when (compile) ...).
;; When it does not appear at top-level, it is similar to the
;; Common Lisp #. reader macro (but not in interpreted code).
;;
;; o The form `eval-and-compile' is similar to eval-when-compile, but
;; the whole form is evalled both at compile-time and at run-time.
;;
;; o The command compile-defun is analogous to eval-defun.
;;
;; o If you run byte-compile-file on a filename which is visited in a
;; buffer, and that buffer is modified, you are asked whether you want
;; to save the buffer before compiling.
;;
;; o byte-compiled files now start with the string `;ELC'.
;; Some versions of `file' can be customized to recognize that.
(require 'backquote)
(require 'macroexp)
(require 'cconv)
;; Refrain from using cl-lib at run-time here, since it otherwise prevents
;; us from emitting warnings when compiling files which use cl-lib without
;; requiring it! (bug#30635)
(eval-when-compile (require 'cl-lib))
;; The feature of compiling in a specific target Emacs version
;; has been turned off because compile time options are a bad idea.
(defgroup bytecomp nil
"Emacs Lisp byte-compiler."
:group 'lisp)
(defcustom emacs-lisp-file-regexp "\\.el\\'"
"Regexp which matches Emacs Lisp source files.
If you change this, you might want to set `byte-compile-dest-file-function'.
\(Note that the assumption of a \".elc\" suffix for compiled files
is hard-coded in various places in Emacs.)"
;; Eg is_elc in Fload.
:group 'bytecomp
:type 'regexp)
(defcustom byte-compile-dest-file-function nil
"Function for the function `byte-compile-dest-file' to call.
It should take one argument, the name of an Emacs Lisp source
file name, and return the name of the compiled file.
\(Note that the assumption that the source and compiled files
are found in the same directory is hard-coded in various places in Emacs.)"
;; Eg load-prefer-newer, documentation lookup IIRC.
:group 'bytecomp
:type '(choice (const nil) function)
:version "23.2")
;; This enables file name handlers such as jka-compr
;; to remove parts of the file name that should not be copied
;; through to the output file name.
(defun byte-compiler-base-file-name (filename)
(let ((handler (find-file-name-handler filename
'byte-compiler-base-file-name)))
(if handler
(funcall handler 'byte-compiler-base-file-name filename)
filename)))
;; Sadly automake relies on this misfeature up to at least version 1.15.1.
(if (fboundp 'byte-compile-dest-file)
(or (featurep 'bytecomp)
(display-warning 'bytecomp (format-message "\
Changing `byte-compile-dest-file' is obsolete (as of 23.2);
set `byte-compile-dest-file-function' instead.")))
(defun byte-compile-dest-file (filename)
"Convert an Emacs Lisp source file name to a compiled file name.
If `byte-compile-dest-file-function' is non-nil, uses that
function to do the work. Otherwise, if FILENAME matches
`emacs-lisp-file-regexp' (by default, files with the extension \".el\"),
replaces the matching part (and anything after it) with \".elc\";
otherwise adds \".elc\"."
(if byte-compile-dest-file-function
(funcall byte-compile-dest-file-function filename)
(setq filename (file-name-sans-versions
(byte-compiler-base-file-name filename)))
(cond ((string-match emacs-lisp-file-regexp filename)
(concat (substring filename 0 (match-beginning 0)) ".elc"))
(t (concat filename ".elc")))))
)
;; This can be the 'byte-compile property of any symbol.
(autoload 'byte-compile-inline-expand "byte-opt")
;; This is the entry point to the lapcode optimizer pass1.
(autoload 'byte-optimize-form "byte-opt")
;; This is the entry point to the lapcode optimizer pass2.
(autoload 'byte-optimize-lapcode "byte-opt")
(autoload 'byte-compile-unfold-lambda "byte-opt")
;; This is the entry point to the decompiler, which is used by the
;; disassembler. The disassembler just requires 'byte-compile, but
;; that doesn't define this function, so this seems to be a reasonable
;; thing to do.
(autoload 'byte-decompile-bytecode "byte-opt")
(defcustom byte-compile-verbose
(and (not noninteractive) (> baud-rate search-slow-speed))
"Non-nil means print messages describing progress of byte-compiler."
:group 'bytecomp
:type 'boolean)
(defcustom byte-optimize t
"Enable optimization in the byte compiler.
Possible values are:
nil - no optimization
t - all optimizations
`source' - source-level optimizations only
`byte' - code-level optimizations only"
:group 'bytecomp
:type '(choice (const :tag "none" nil)
(const :tag "all" t)
(const :tag "source-level" source)
(const :tag "byte-level" byte)))
(defcustom byte-compile-delete-errors nil
"If non-nil, the optimizer may delete forms that may signal an error.
This includes variable references and calls to functions such as `car'."
:group 'bytecomp
:type 'boolean)
(defcustom byte-compile-cond-use-jump-table t
"Compile `cond' clauses to a jump table implementation (using a hash-table)."
:version "26.1"
:group 'bytecomp
:type 'boolean)
(defvar byte-compile-dynamic nil
"If non-nil, compile function bodies so they load lazily.
They are hidden in comments in the compiled file,
and each one is 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.")
;;;###autoload(put 'byte-compile-dynamic 'safe-local-variable 'booleanp)
(defvar byte-compile-disable-print-circle nil
"If non-nil, disable `print-circle' on printing a byte-compiled code.")
(make-obsolete-variable 'byte-compile-disable-print-circle nil "24.1")
;;;###autoload(put 'byte-compile-disable-print-circle 'safe-local-variable 'booleanp)
(defcustom 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.
To disable this option for a certain file, make it a file-local variable
in the source file. For example, add this to the first line:
-*-byte-compile-dynamic-docstrings:nil;-*-
You can also set the variable globally.
This option is enabled by default because it reduces Emacs memory usage."
:group 'bytecomp
:type 'boolean)
;;;###autoload(put 'byte-compile-dynamic-docstrings 'safe-local-variable 'booleanp)
(defconst byte-compile-log-buffer "*Compile-Log*"
"Name of the byte-compiler's log buffer.")
(defcustom byte-optimize-log nil
"If non-nil, the byte-compiler will log its optimizations.
If this is `source', then only source-level optimizations will be logged.
If it is `byte', then only byte-level optimizations will be logged.
The information is logged to `byte-compile-log-buffer'."
:group 'bytecomp
:type '(choice (const :tag "none" nil)
(const :tag "all" t)
(const :tag "source-level" source)
(const :tag "byte-level" byte)))
(defcustom byte-compile-error-on-warn nil
"If true, the byte-compiler reports warnings with `error'."
:group 'bytecomp
:type 'boolean)
;; This needs to be autoloaded because it needs to be available to
;; Emacs before the byte compiler is loaded, otherwise Emacs will not
;; know that this variable is marked as safe until it is too late.
;; (See https://lists.gnu.org/archive/html/emacs-devel/2018-01/msg00261.html )
;;;###autoload(put 'byte-compile-error-on-warn 'safe-local-variable 'booleanp)
(defconst byte-compile-warning-types
'(redefine callargs free-vars unresolved
obsolete noruntime cl-functions interactive-only
make-local mapcar constants suspicious lexical)
"The list of warning types used when `byte-compile-warnings' is t.")
(defcustom byte-compile-warnings t
"List of warnings that the byte-compiler should issue (t for all).
Elements of the list may be:
free-vars references to variables not in the current lexical scope.
unresolved calls to unknown functions.
callargs function calls with args that don't match the definition.
redefine function name redefined from a macro to ordinary function or vice
versa, or redefined to take a different number of arguments.
obsolete obsolete variables and functions.
noruntime functions that may not be defined at runtime (typically
defined only under `eval-when-compile').
cl-functions calls to runtime functions (as distinguished from macros and
aliases) from the old CL package (not the newer cl-lib).
interactive-only
commands that normally shouldn't be called from Lisp code.
lexical global/dynamic variables lacking a prefix.
make-local calls to make-variable-buffer-local that may be incorrect.
mapcar mapcar called for effect.
constants let-binding of, or assignment to, constants/nonvariables.
suspicious constructs that usually don't do what the coder wanted.
If the list begins with `not', then the remaining elements specify warnings to
suppress. For example, (not mapcar) will suppress warnings about mapcar."
:group 'bytecomp
:type `(choice (const :tag "All" t)
(set :menu-tag "Some"
,@(mapcar (lambda (x) `(const ,x))
byte-compile-warning-types))))
;;;###autoload
(put 'byte-compile-warnings 'safe-local-variable
(lambda (v)
(or (symbolp v)
(null (delq nil (mapcar (lambda (x) (not (symbolp x))) v))))))
(defun byte-compile-warning-enabled-p (warning)
"Return non-nil if WARNING is enabled, according to `byte-compile-warnings'."
(or (eq byte-compile-warnings t)
(if (eq (car byte-compile-warnings) 'not)
(not (memq warning byte-compile-warnings))
(memq warning byte-compile-warnings))))
;;;###autoload
(defun byte-compile-disable-warning (warning)
"Change `byte-compile-warnings' to disable WARNING.
If `byte-compile-warnings' is t, set it to `(not WARNING)'.
Otherwise, if the first element is `not', add WARNING, else remove it.
Normally you should let-bind `byte-compile-warnings' before calling this,
else the global value will be modified."
(setq byte-compile-warnings
(cond ((eq byte-compile-warnings t)
(list 'not warning))
((eq (car byte-compile-warnings) 'not)
(if (memq warning byte-compile-warnings)
byte-compile-warnings
(append byte-compile-warnings (list warning))))
(t
(delq warning byte-compile-warnings)))))
;;;###autoload
(defun byte-compile-enable-warning (warning)
"Change `byte-compile-warnings' to enable WARNING.
If `byte-compile-warnings' is t, do nothing. Otherwise, if the
first element is `not', remove WARNING, else add it.
Normally you should let-bind `byte-compile-warnings' before calling this,
else the global value will be modified."
(or (eq byte-compile-warnings t)
(setq byte-compile-warnings
(cond ((eq (car byte-compile-warnings) 'not)
(delq warning byte-compile-warnings))
((memq warning byte-compile-warnings)
byte-compile-warnings)
(t
(append byte-compile-warnings (list warning)))))))
(defvar byte-compile-interactive-only-functions nil
"List of commands that are not meant to be called from Lisp.")
(make-obsolete-variable 'byte-compile-interactive-only-functions
"use the `interactive-only' symbol property instead."
"24.4")
(defvar byte-compile-not-obsolete-vars nil
"List of variables that shouldn't be reported as obsolete.")
(defvar byte-compile-global-not-obsolete-vars nil
"Global list of variables that shouldn't be reported as obsolete.")
(defvar byte-compile-not-obsolete-funcs nil
"List of functions that shouldn't be reported as obsolete.")
(defcustom byte-compile-generate-call-tree nil
"Non-nil means collect call-graph information when compiling.
This records which functions were called and from where.
If the value is t, compilation displays the call graph when it finishes.
If the value is neither t nor nil, compilation asks you whether to display
the graph.
The call tree only lists functions called, not macros used. Those functions
which the byte-code interpreter knows about directly (eq, cons, etc.) are
not reported.
The call tree also lists those functions which are not known to be called
\(that is, to which no calls have been compiled). Functions which can be
invoked interactively are excluded from this list."
:group 'bytecomp
:type '(choice (const :tag "Yes" t) (const :tag "No" nil)
(other :tag "Ask" lambda)))
(defvar byte-compile-call-tree nil
"Alist of functions and their call tree.
Each element looks like
(FUNCTION CALLERS CALLS)
where CALLERS is a list of functions that call FUNCTION, and CALLS
is a list of functions for which calls were generated while compiling
FUNCTION.")
(defcustom byte-compile-call-tree-sort 'name
"If non-nil, sort the call tree.
The values `name', `callers', `calls', `calls+callers'
specify different fields to sort on."
:group 'bytecomp
:type '(choice (const name) (const callers) (const calls)
(const calls+callers) (const nil)))
(defvar byte-compile-debug nil
"If non-nil, byte compile errors will be raised as signals instead of logged.")
(defvar byte-compile-jump-tables nil
"List of all jump tables used during compilation of this form.")
(defvar byte-compile-constants nil
"List of all constants encountered during compilation of this form.")
(defvar byte-compile-variables nil
"List of all variables encountered during compilation of this form.")
(defvar byte-compile-bound-variables nil
"List of dynamic variables bound in the context of the current form.
This list lives partly on the stack.")
(defvar byte-compile-lexical-variables nil
"List of variables that have been treated as lexical.
Filled in `cconv-analyze-form' but initialized and consulted here.")
(defvar byte-compile-const-variables nil
"List of variables declared as constants during compilation of this file.")
(defvar byte-compile-free-references)
(defvar byte-compile-free-assignments)
(defvar byte-compiler-error-flag)
(defun byte-compile-recurse-toplevel (form non-toplevel-case)
"Implement `eval-when-compile' and `eval-and-compile'.
Return the compile-time value of FORM."
;; Macroexpand (not macroexpand-all!) form at toplevel in case it
;; expands into a toplevel-equivalent `progn'. See CLHS section
;; 3.2.3.1, "Processing of Top Level Forms". The semantics are very
;; subtle: see test/lisp/emacs-lisp/bytecomp-tests.el for interesting
;; cases.
(setf form (macroexp-macroexpand form byte-compile-macro-environment))
(if (eq (car-safe form) 'progn)
(cons 'progn
(mapcar (lambda (subform)
(byte-compile-recurse-toplevel
subform non-toplevel-case))
(cdr form)))
(funcall non-toplevel-case form)))
(defconst byte-compile-initial-macro-environment
`(
;; (byte-compiler-options . (lambda (&rest forms)
;; (apply 'byte-compiler-options-handler forms)))
(declare-function . byte-compile-macroexpand-declare-function)
(eval-when-compile . ,(lambda (&rest body)
(let ((result nil))
(byte-compile-recurse-toplevel
(macroexp-progn body)
(lambda (form)
;; Insulate the following variables
;; against changes made in the
;; subsidiary compilation. This
;; prevents spurious warning
;; messages: "not defined at runtime"
;; etc.
(let ((byte-compile-unresolved-functions
byte-compile-unresolved-functions)
(byte-compile-new-defuns
byte-compile-new-defuns))
(setf result
(byte-compile-eval
(byte-compile-top-level
(byte-compile-preprocess form)))))))
(list 'quote result))))
(eval-and-compile . ,(lambda (&rest body)
(byte-compile-recurse-toplevel
(macroexp-progn body)
(lambda (form)
;; Don't compile here, since we don't know
;; whether to compile as byte-compile-form
;; or byte-compile-file-form.
(let ((expanded
(macroexpand-all
form
macroexpand-all-environment)))
(eval expanded lexical-binding)
expanded))))))
"The default macro-environment passed to macroexpand by the compiler.
Placing a macro here will cause a macro to have different semantics when
expanded by the compiler as when expanded by the interpreter.")
(defvar byte-compile-macro-environment byte-compile-initial-macro-environment
"Alist of macros defined in the file being compiled.
Each element looks like (MACRONAME . DEFINITION). It is
\(MACRONAME . nil) when a macro is redefined as a function.")
(defvar byte-compile-function-environment nil
"Alist of functions defined in the file being compiled.
This is so we can inline them when necessary.
Each element looks like (FUNCTIONNAME . DEFINITION). It is
\(FUNCTIONNAME . nil) when a function is redefined as a macro.
It is \(FUNCTIONNAME . t) when all we know is that it was defined,
and we don't know the definition. For an autoloaded function, DEFINITION
has the form (autoload . FILENAME).")
(defvar byte-compile-unresolved-functions nil
"Alist of undefined functions to which calls have been compiled.
This variable is only significant whilst compiling an entire buffer.
Used for warnings when a function is not known to be defined or is later
defined with incorrect args.")
(defvar byte-compile-noruntime-functions nil
"Alist of functions called that may not be defined when the compiled code is run.
Used for warnings about calling a function that is defined during compilation
but won't necessarily be defined when the compiled file is loaded.")
(defvar byte-compile-new-defuns nil
"List of (runtime) functions defined in this compilation run.
This variable is used to qualify `byte-compile-noruntime-functions' when
outputting warnings about functions not being defined at runtime.")
;; Variables for lexical binding
(defvar byte-compile--lexical-environment nil
"The current lexical environment.")
(defvar byte-compile-tag-number 0)
(defvar byte-compile-output nil
"Alist describing contents to put in byte code string.
Each element is (INDEX . VALUE)")
(defvar byte-compile-depth 0 "Current depth of execution stack.")
(defvar byte-compile-maxdepth 0 "Maximum depth of execution stack.")
;;; The byte codes; this information is duplicated in bytecomp.c
(defvar byte-code-vector nil
"An array containing byte-code names indexed by byte-code values.")
(defvar byte-stack+-info nil
"An array with the stack adjustment for each byte-code.")
(defmacro byte-defop (opcode stack-adjust opname &optional docstring)
;; This is a speed-hack for building the byte-code-vector at compile-time.
;; We fill in the vector at macroexpand-time, and then after the last call
;; to byte-defop, we write the vector out as a constant instead of writing
;; out a bunch of calls to aset.
;; Actually, we don't fill in the vector itself, because that could make
;; it problematic to compile big changes to this compiler; we store the
;; values on its plist, and remove them later in -extrude.
(let ((v1 (or (get 'byte-code-vector 'tmp-compile-time-value)
(put 'byte-code-vector 'tmp-compile-time-value
(make-vector 256 nil))))
(v2 (or (get 'byte-stack+-info 'tmp-compile-time-value)
(put 'byte-stack+-info 'tmp-compile-time-value
(make-vector 256 nil)))))
(aset v1 opcode opname)
(aset v2 opcode stack-adjust))
(if docstring
(list 'defconst opname opcode (concat "Byte code opcode " docstring "."))
(list 'defconst opname opcode)))
(defmacro byte-extrude-byte-code-vectors ()
(prog1 (list 'setq 'byte-code-vector
(get 'byte-code-vector 'tmp-compile-time-value)
'byte-stack+-info
(get 'byte-stack+-info 'tmp-compile-time-value))
(put 'byte-code-vector 'tmp-compile-time-value nil)
(put 'byte-stack+-info 'tmp-compile-time-value nil)))
;; These opcodes are special in that they pack their argument into the
;; opcode word.
;;
(byte-defop 0 1 byte-stack-ref "for stack reference")
(byte-defop 8 1 byte-varref "for variable reference")
(byte-defop 16 -1 byte-varset "for setting a variable")
(byte-defop 24 -1 byte-varbind "for binding a variable")
(byte-defop 32 0 byte-call "for calling a function")
(byte-defop 40 0 byte-unbind "for unbinding special bindings")
;; codes 8-47 are consumed by the preceding opcodes
;; New (in Emacs-24.4) bytecodes for more efficient handling of non-local exits
;; (especially useful in lexical-binding code).
(byte-defop 48 0 byte-pophandler)
(byte-defop 50 -1 byte-pushcatch)
(byte-defop 49 -1 byte-pushconditioncase)
;; unused: 51-55
(byte-defop 56 -1 byte-nth)
(byte-defop 57 0 byte-symbolp)
(byte-defop 58 0 byte-consp)
(byte-defop 59 0 byte-stringp)
(byte-defop 60 0 byte-listp)
(byte-defop 61 -1 byte-eq)
(byte-defop 62 -1 byte-memq)
(byte-defop 63 0 byte-not)
(byte-defop 64 0 byte-car)
(byte-defop 65 0 byte-cdr)
(byte-defop 66 -1 byte-cons)
(byte-defop 67 0 byte-list1)
(byte-defop 68 -1 byte-list2)
(byte-defop 69 -2 byte-list3)
(byte-defop 70 -3 byte-list4)
(byte-defop 71 0 byte-length)
(byte-defop 72 -1 byte-aref)
(byte-defop 73 -2 byte-aset)
(byte-defop 74 0 byte-symbol-value)
(byte-defop 75 0 byte-symbol-function) ; this was commented out
(byte-defop 76 -1 byte-set)
(byte-defop 77 -1 byte-fset) ; this was commented out
(byte-defop 78 -1 byte-get)
(byte-defop 79 -2 byte-substring)
(byte-defop 80 -1 byte-concat2)
(byte-defop 81 -2 byte-concat3)
(byte-defop 82 -3 byte-concat4)
(byte-defop 83 0 byte-sub1)
(byte-defop 84 0 byte-add1)
(byte-defop 85 -1 byte-eqlsign)
(byte-defop 86 -1 byte-gtr)
(byte-defop 87 -1 byte-lss)
(byte-defop 88 -1 byte-leq)
(byte-defop 89 -1 byte-geq)
(byte-defop 90 -1 byte-diff)
(byte-defop 91 0 byte-negate)
(byte-defop 92 -1 byte-plus)
(byte-defop 93 -1 byte-max)
(byte-defop 94 -1 byte-min)
(byte-defop 95 -1 byte-mult) ; v19 only
(byte-defop 96 1 byte-point)
(byte-defop 98 0 byte-goto-char)
(byte-defop 99 0 byte-insert)
(byte-defop 100 1 byte-point-max)
(byte-defop 101 1 byte-point-min)
(byte-defop 102 0 byte-char-after)
(byte-defop 103 1 byte-following-char)
(byte-defop 104 1 byte-preceding-char)
(byte-defop 105 1 byte-current-column)
(byte-defop 106 0 byte-indent-to)
(byte-defop 107 0 byte-scan-buffer-OBSOLETE) ; no longer generated as of v18
(byte-defop 108 1 byte-eolp)
(byte-defop 109 1 byte-eobp)
(byte-defop 110 1 byte-bolp)
(byte-defop 111 1 byte-bobp)
(byte-defop 112 1 byte-current-buffer)
(byte-defop 113 0 byte-set-buffer)
(byte-defop 114 0 byte-save-current-buffer
"To make a binding to record the current buffer")
(byte-defop 115 0 byte-set-mark-OBSOLETE)
(byte-defop 116 1 byte-interactive-p-OBSOLETE)
;; These ops are new to v19
(byte-defop 117 0 byte-forward-char)
(byte-defop 118 0 byte-forward-word)
(byte-defop 119 -1 byte-skip-chars-forward)
(byte-defop 120 -1 byte-skip-chars-backward)
(byte-defop 121 0 byte-forward-line)
(byte-defop 122 0 byte-char-syntax)
(byte-defop 123 -1 byte-buffer-substring)
(byte-defop 124 -1 byte-delete-region)
(byte-defop 125 -1 byte-narrow-to-region)
(byte-defop 126 1 byte-widen)
(byte-defop 127 0 byte-end-of-line)
;; unused: 128
;; These store their argument in the next two bytes
(byte-defop 129 1 byte-constant2
"for reference to a constant with vector index >= byte-constant-limit")
(byte-defop 130 0 byte-goto "for unconditional jump")
(byte-defop 131 -1 byte-goto-if-nil "to pop value and jump if it's nil")
(byte-defop 132 -1 byte-goto-if-not-nil "to pop value and jump if it's not nil")
(byte-defop 133 -1 byte-goto-if-nil-else-pop
"to examine top-of-stack, jump and don't pop it if it's nil,
otherwise pop it")
(byte-defop 134 -1 byte-goto-if-not-nil-else-pop
"to examine top-of-stack, jump and don't pop it if it's non nil,
otherwise pop it")
(byte-defop 135 -1 byte-return "to pop a value and return it from `byte-code'")
(byte-defop 136 -1 byte-discard "to discard one value from stack")
(byte-defop 137 1 byte-dup "to duplicate the top of the stack")
(byte-defop 138 0 byte-save-excursion
"to make a binding to record the buffer, point and mark")
(byte-defop 139 0 byte-save-window-excursion-OBSOLETE
"to make a binding to record entire window configuration")
(byte-defop 140 0 byte-save-restriction
"to make a binding to record the current buffer clipping restrictions")
(byte-defop 141 -1 byte-catch
"for catch. Takes, on stack, the tag and an expression for the body")
(byte-defop 142 -1 byte-unwind-protect
"for unwind-protect. Takes, on stack, an expression for the unwind-action")
;; For condition-case. Takes, on stack, the variable to bind,
;; an expression for the body, and a list of clauses.
(byte-defop 143 -2 byte-condition-case)
(byte-defop 144 0 byte-temp-output-buffer-setup-OBSOLETE)
(byte-defop 145 -1 byte-temp-output-buffer-show-OBSOLETE)
;; these ops are new to v19
;; To unbind back to the beginning of this frame.
;; Not used yet, but will be needed for tail-recursion elimination.
(byte-defop 146 0 byte-unbind-all)
;; these ops are new to v19
(byte-defop 147 -2 byte-set-marker)
(byte-defop 148 0 byte-match-beginning)
(byte-defop 149 0 byte-match-end)
(byte-defop 150 0 byte-upcase)
(byte-defop 151 0 byte-downcase)
(byte-defop 152 -1 byte-string=)
(byte-defop 153 -1 byte-string<)
(byte-defop 154 -1 byte-equal)
(byte-defop 155 -1 byte-nthcdr)
(byte-defop 156 -1 byte-elt)
(byte-defop 157 -1 byte-member)
(byte-defop 158 -1 byte-assq)
(byte-defop 159 0 byte-nreverse)
(byte-defop 160 -1 byte-setcar)
(byte-defop 161 -1 byte-setcdr)
(byte-defop 162 0 byte-car-safe)
(byte-defop 163 0 byte-cdr-safe)
(byte-defop 164 -1 byte-nconc)
(byte-defop 165 -1 byte-quo)
(byte-defop 166 -1 byte-rem)
(byte-defop 167 0 byte-numberp)
(byte-defop 168 0 byte-integerp)
;; unused: 169-174
(byte-defop 175 nil byte-listN)
(byte-defop 176 nil byte-concatN)
(byte-defop 177 nil byte-insertN)
(byte-defop 178 -1 byte-stack-set) ; Stack offset in following one byte.
(byte-defop 179 -1 byte-stack-set2) ; Stack offset in following two bytes.
;; If (following one byte & 0x80) == 0
;; discard (following one byte & 0x7F) stack entries
;; else
;; discard (following one byte & 0x7F) stack entries _underneath_ TOS
;; (that is, if the operand = 0x83, ... X Y Z T => ... T)
(byte-defop 182 nil byte-discardN)
;; `byte-discardN-preserve-tos' is a pseudo-op that gets turned into
;; `byte-discardN' with the high bit in the operand set (by
;; `byte-compile-lapcode').
(defconst byte-discardN-preserve-tos byte-discardN)
(byte-defop 183 -2 byte-switch
"to take a hash table and a value from the stack, and jump to the address
the value maps to, if any.")
;; unused: 182-191
(byte-defop 192 1 byte-constant "for reference to a constant")
;; codes 193-255 are consumed by byte-constant.
(defconst byte-constant-limit 64
"Exclusive maximum index usable in the `byte-constant' opcode.")
(defconst byte-goto-ops '(byte-goto byte-goto-if-nil byte-goto-if-not-nil
byte-goto-if-nil-else-pop
byte-goto-if-not-nil-else-pop
byte-pushcatch byte-pushconditioncase)
"List of byte-codes whose offset is a pc.")
(defconst byte-goto-always-pop-ops '(byte-goto-if-nil byte-goto-if-not-nil))
(byte-extrude-byte-code-vectors)
;;; lapcode generator
;;
;; the byte-compiler now does source -> lapcode -> bytecode instead of
;; source -> bytecode, because it's a lot easier to make optimizations
;; on lapcode than on bytecode.
;;
;; Elements of the lapcode list are of the form (<instruction> . <parameter>)
;; where instruction is a symbol naming a byte-code instruction,
;; and parameter is an argument to that instruction, if any.
;;
;; The instruction can be the pseudo-op TAG, which means that this position
;; in the instruction stream is a target of a goto. (car PARAMETER) will be
;; the PC for this location, and the whole instruction "(TAG pc)" will be the
;; parameter for some goto op.
;;
;; If the operation is varbind, varref, varset or push-constant, then the
;; parameter is (variable/constant . index_in_constant_vector).
;;
;; First, the source code is macroexpanded and optimized in various ways.
;; Then the resultant code is compiled into lapcode. Another set of
;; optimizations are then run over the lapcode. Then the variables and
;; constants referenced by the lapcode are collected and placed in the
;; constants-vector. (This happens now so that variables referenced by dead
;; code don't consume space.) And finally, the lapcode is transformed into
;; compacted byte-code.
;;
;; A distinction is made between variables and constants because the variable-
;; referencing instructions are more sensitive to the variables being near the
;; front of the constants-vector than the constant-referencing instructions.
;; Also, this lets us notice references to free variables.
(defmacro byte-compile-push-bytecodes (&rest args)
"Push bytes onto BVAR, and increment CVAR by the number of bytes pushed.
BVAR and CVAR are variables which are updated after evaluating
all the arguments.
\(fn BYTE1 BYTE2 ... BYTEn BVAR CVAR)"
(let ((byte-exprs (butlast args 2))
(bytes-var (car (last args 2)))
(pc-var (car (last args))))
`(setq ,bytes-var ,(if (null (cdr byte-exprs))
`(progn (cl-assert (<= 0 ,(car byte-exprs)))
(cons ,@byte-exprs ,bytes-var))
`(nconc (list ,@(reverse byte-exprs)) ,bytes-var))
,pc-var (+ ,(length byte-exprs) ,pc-var))))
(defmacro byte-compile-push-bytecode-const2 (opcode const2 bytes pc)
"Push OPCODE and the two-byte constant CONST2 onto BYTES, and add 3 to PC.
CONST2 may be evaluated multiple times."
`(byte-compile-push-bytecodes ,opcode (logand ,const2 255) (lsh ,const2 -8)
,bytes ,pc))
(defun byte-compile-lapcode (lap)
"Turns lapcode into bytecode. The lapcode is destroyed."
;; Lapcode modifications: changes the ID of a tag to be the tag's PC.
(let ((pc 0) ; Program counter
op off ; Operation & offset
opcode ; numeric value of OP
(bytes '()) ; Put the output bytes here
(patchlist nil)) ; List of gotos to patch
(dolist (lap-entry lap)
(setq op (car lap-entry)
off (cdr lap-entry))
(cond
((not (symbolp op))
(error "Non-symbolic opcode `%s'" op))
((eq op 'TAG)
(setcar off pc))
(t
(setq opcode
(if (eq op 'byte-discardN-preserve-tos)
;; byte-discardN-preserve-tos is a pseudo op, which
;; is actually the same as byte-discardN
;; with a modified argument.
byte-discardN
(symbol-value op)))
(cond ((memq op byte-goto-ops)
;; goto
(byte-compile-push-bytecodes opcode nil (cdr off) bytes pc)
(push bytes patchlist))
((or (and (consp off)
;; Variable or constant reference
(progn
(setq off (cdr off))
(eq op 'byte-constant)))
(and (eq op 'byte-constant)
(integerp off)))
;; constant ref
(if (< off byte-constant-limit)
(byte-compile-push-bytecodes (+ byte-constant off)
bytes pc)
(byte-compile-push-bytecode-const2 byte-constant2 off
bytes pc)))
((and (= opcode byte-stack-set)
(> off 255))
;; Use the two-byte version of byte-stack-set if the
;; offset is too large for the normal version.
(byte-compile-push-bytecode-const2 byte-stack-set2 off
bytes pc))
((and (>= opcode byte-listN)
(< opcode byte-discardN))
;; These insns all put their operand into one extra byte.
(byte-compile-push-bytecodes opcode off bytes pc))
((= opcode byte-discardN)
;; byte-discardN is weird in that it encodes a flag in the
;; top bit of its one-byte argument. If the argument is
;; too large to fit in 7 bits, the opcode can be repeated.
(let ((flag (if (eq op 'byte-discardN-preserve-tos) #x80 0)))
(while (> off #x7f)
(byte-compile-push-bytecodes opcode (logior #x7f flag)
bytes pc)
(setq off (- off #x7f)))
(byte-compile-push-bytecodes opcode (logior off flag)
bytes pc)))
((null off)
;; opcode that doesn't use OFF
(byte-compile-push-bytecodes opcode bytes pc))
((and (eq opcode byte-stack-ref) (eq off 0))
;; (stack-ref 0) is really just another name for `dup'.
(debug) ;FIXME: When would this happen?
(byte-compile-push-bytecodes byte-dup bytes pc))
;; The following three cases are for the special
;; insns that encode their operand into 0, 1, or 2
;; extra bytes depending on its magnitude.
((< off 6)
(byte-compile-push-bytecodes (+ opcode off) bytes pc))
((< off 256)
(byte-compile-push-bytecodes (+ opcode 6) off bytes pc))
(t
(byte-compile-push-bytecode-const2 (+ opcode 7) off
bytes pc))))))
;;(if (not (= pc (length bytes)))
;; (error "Compiler error: pc mismatch - %s %s" pc (length bytes)))
;; Patch tag PCs into absolute jumps.
(dolist (bytes-tail patchlist)
(setq pc (caar bytes-tail)) ; Pick PC from goto's tag.
;; Splits PC's value into 2 bytes. The jump address is
;; "reconstructed" by the `FETCH2' macro in `bytecode.c'.
(setcar (cdr bytes-tail) (logand pc 255))
(setcar bytes-tail (lsh pc -8))
;; FIXME: Replace this by some workaround.
(if (> (car bytes-tail) 255) (error "Bytecode overflow")))
;; Similarly, replace TAGs in all jump tables with the correct PC index.
(dolist (hash-table byte-compile-jump-tables)
(maphash #'(lambda (value tag)
(setq pc (cadr tag))
;; We don't need to split PC here, as it is stored as a lisp
;; object in the hash table (whereas other goto-* ops store
;; it within 2 bytes in the byte string).
(puthash value pc hash-table))
hash-table))
(apply 'unibyte-string (nreverse bytes))))
;;; compile-time evaluation
(defun byte-compile-cl-file-p (file)
"Return non-nil if FILE is one of the CL files."
(and (stringp file)
(string-match "^cl\\.el" (file-name-nondirectory file))))
(defun byte-compile-eval (form)
"Eval FORM and mark the functions defined therein.
Each function's symbol gets added to `byte-compile-noruntime-functions'."
(let ((hist-orig load-history)
(hist-nil-orig current-load-list))
(prog1 (eval form lexical-binding)
(when (byte-compile-warning-enabled-p 'noruntime)
(let ((hist-new load-history)
(hist-nil-new current-load-list))
;; Go through load-history, look for newly loaded files
;; and mark all the functions defined therein.
(while (and hist-new (not (eq hist-new hist-orig)))
(let ((xs (pop hist-new))
old-autoloads)
;; Make sure the file was not already loaded before.
(unless (assoc (car xs) hist-orig)
(dolist (s xs)
(cond
((and (consp s) (eq t (car s)))
(push (cdr s) old-autoloads))
((and (consp s) (memq (car s) '(autoload defun)))
(unless (memq (cdr s) old-autoloads)
(push (cdr s) byte-compile-noruntime-functions))))))))
;; Go through current-load-list for the locally defined funs.
(let (old-autoloads)
(while (and hist-nil-new (not (eq hist-nil-new hist-nil-orig)))
(let ((s (pop hist-nil-new)))
(when (and (symbolp s) (not (memq s old-autoloads)))
(push s byte-compile-noruntime-functions))
(when (and (consp s) (eq t (car s)))
(push (cdr s) old-autoloads)))))))
(when (byte-compile-warning-enabled-p 'cl-functions)
(let ((hist-new load-history))
;; Go through load-history, looking for the cl files.
;; Since new files are added at the start of load-history,
;; we scan the new history until the tail matches the old.
(while (and (not byte-compile-cl-functions)
hist-new (not (eq hist-new hist-orig)))
;; We used to check if the file had already been loaded,
;; but it is better to check non-nil byte-compile-cl-functions.
(and (byte-compile-cl-file-p (car (pop hist-new)))
(byte-compile-find-cl-functions))))))))
(defun byte-compile-eval-before-compile (form)
"Evaluate FORM for `eval-and-compile'."
(let ((hist-nil-orig current-load-list))
(prog1 (eval form lexical-binding)
;; (eval-and-compile (require 'cl) turns off warnings for cl functions.
;; FIXME Why does it do that - just as a hack?
;; There are other ways to do this nowadays.