-
Notifications
You must be signed in to change notification settings - Fork 0
/
xref.lisp
2891 lines (2712 loc) · 123 KB
/
xref.lisp
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
;;; -*- Mode: LISP; Package: XREF; Syntax: Common-lisp; -*-
;;; Mon Jan 21 16:21:20 1991 by Mark Kantrowitz <mkant@GLINDA.OZ.CS.CMU.EDU>
;;; xref.lisp
;;; ****************************************************************
;;; List Callers: A Static Analysis Cross Referencing Tool for Lisp
;;; ****************************************************************
;;;
;;; The List Callers system is a portable Common Lisp cross referencing
;;; utility. It grovels over a set of files and compiles a database of the
;;; locations of all references for each symbol used in the files.
;;; List Callers is similar to the Symbolics Who-Calls and the
;;; Xerox Masterscope facilities.
;;;
;;; When you change a function or variable definition, it can be useful
;;; to know its callers, in order to update each of them to the new
;;; definition. Similarly, having a graphic display of the structure
;;; (e.g., call graph) of a program can help make undocumented code more
;;; understandable. This static code analyzer facilitates both capabilities.
;;; The database compiled by xref is suitable for viewing by a graphical
;;; browser. (Note: the reference graph is not necessarily a DAG. Since many
;;; graphical browsers assume a DAG, this will lead to infinite loops.
;;; Some code which is useful in working around this problem is included,
;;; as well as a sample text-indenting outliner and an interface to Bates'
;;; PSGraph Postscript Graphing facility.)
;;;
;;; Written by Mark Kantrowitz, July 1990.
;;;
;;; Address: School of Computer Science
;;; Carnegie Mellon University
;;; Pittsburgh, PA 15213
;;;
;;; Copyright (c) 1990. All rights reserved.
;;;
;;; See general license below.
;;;
;;; ****************************************************************
;;; General License Agreement and Lack of Warranty *****************
;;; ****************************************************************
;;;
;;; This software is distributed in the hope that it will be useful (both
;;; in and of itself and as an example of lisp programming), but WITHOUT
;;; ANY WARRANTY. The author(s) do not accept responsibility to anyone for
;;; the consequences of using it or for whether it serves any particular
;;; purpose or works at all. No warranty is made about the software or its
;;; performance.
;;;
;;; Use and copying of this software and the preparation of derivative
;;; works based on this software are permitted, so long as the following
;;; conditions are met:
;;; o The copyright notice and this entire notice are included intact
;;; and prominently carried on all copies and supporting documentation.
;;; o No fees or compensation are charged for use, copies, or
;;; access to this software. You may charge a nominal
;;; distribution fee for the physical act of transferring a
;;; copy, but you may not charge for the program itself.
;;; o If you modify this software, you must cause the modified
;;; file(s) to carry prominent notices (a Change Log)
;;; describing the changes, who made the changes, and the date
;;; of those changes.
;;; o Any work distributed or published that in whole or in part
;;; contains or is a derivative of this software or any part
;;; thereof is subject to the terms of this agreement. The
;;; aggregation of another unrelated program with this software
;;; or its derivative on a volume of storage or distribution
;;; medium does not bring the other program under the scope
;;; of these terms.
;;; o Permission is granted to manufacturers and distributors of
;;; lisp compilers and interpreters to include this software
;;; with their distribution.
;;;
;;; This software is made available AS IS, and is distributed without
;;; warranty of any kind, either expressed or implied.
;;;
;;; In no event will the author(s) or their institutions be liable to you
;;; for damages, including lost profits, lost monies, or other special,
;;; incidental or consequential damages arising out of or in connection
;;; with the use or inability to use (including but not limited to loss of
;;; data or data being rendered inaccurate or losses sustained by third
;;; parties or a failure of the program to operate as documented) the
;;; program, even if you have been advised of the possibility of such
;;; damanges, or for any claim by any other party, whether in an action of
;;; contract, negligence, or other tortious action.
;;;
;;; The current version of this software and a variety of related utilities
;;; may be obtained by anonymous ftp from ftp.cs.cmu.edu in the directory
;;; user/ai/lang/lisp/code/tools/xref/
;;;
;;; Please send bug reports, comments, questions and suggestions to
;;; mkant@cs.cmu.edu. We would also appreciate receiving any changes
;;; or improvements you may make.
;;;
;;; If you wish to be added to the Lisp-Utilities@cs.cmu.edu mailing list,
;;; send email to Lisp-Utilities-Request@cs.cmu.edu with your name, email
;;; address, and affiliation. This mailing list is primarily for
;;; notification about major updates, bug fixes, and additions to the lisp
;;; utilities collection. The mailing list is intended to have low traffic.
;;;
;;; ********************************
;;; Change Log *********************
;;; ********************************
;;;
;;; 27-FEB-91 mk Added insert arg to psgraph-xref to allow the postscript
;;; graphs to be inserted in Scribe documents.
;;; 21-FEB-91 mk Added warning if not compiled.
;;; 07-FEB-91 mk Fixed bug in record-callers with regard to forms at
;;; toplevel.
;;; 21-JAN-91 mk Added file xref-test.lisp to test xref.
;;; 16-JAN-91 mk Added definition WHO-CALLS to parallel the Symbolics syntax.
;;; 16-JAN-91 mk Added macroexpansion capability to record-callers. Also
;;; added parameter *handle-macro-forms*, defaulting to T.
;;; 16-JAN-91 mk Modified print-caller-tree and related functions
;;; to allow the user to specify root nodes. If the user
;;; doesn't specify them, it will default to all root
;;; nodes, as before.
;;; 16-JAN-91 mk Added parameter *default-graphing-mode* to specify
;;; the direction of the graphing. Either :call-graph,
;;; where the children of a node are those functions called
;;; by the node, or :caller-graph where the children of a
;;; node are the callers of the node. :call-graph is the
;;; default.
;;; 16-JAN-91 mk Added parameter *indent-amount* to control the indentation
;;; in print-indented-tree.
;;; 16-JUL-90 mk Functions with argument lists of () were being ignored
;;; because of a (when form) wrapped around the body of
;;; record-callers. Then intent of (when form) was as an extra
;;; safeguard against infinite looping. This wasn't really
;;; necessary, so it has been removed.
;;; 16-JUL-90 mk PSGraph-XREF now has keyword arguments, instead of
;;; optionals.
;;; 16-JUL-90 mk Added PRINT-CLASS-HIERARCHY to use psgraph to graph the
;;; CLOS class hierarchy. This really doesn't belong here,
;;; and should be moved to psgraph.lisp as an example of how
;;; to use psgraph.
;;; 16-JUL-90 mk Fixed several caller patterns. The pattern for member
;;; had an error which caused many references to be missed.
;;; 16-JUL-90 mk Added ability to save/load processed databases.
;;; 5-JUL-91 mk Fixed warning of needing compilation to occur only when the
;;; source is loaded.
;;; 20-SEP-93 mk Added fix from Peter Norvig to allow Xref to xref itself.
;;; The arg to macro-function must be a symbol.
;;; ********************************
;;; To Do **************************
;;; ********************************
;;;
;;; Verify that:
;;; o null forms don't cause it to infinite loop.
;;; o nil matches against null argument lists.
;;; o declarations and doc are being ignored.
;;;
;;; Would be nice if in addition to showing callers of a function, it
;;; displayed the context of the calls to the function (e.g., the
;;; immediately surrounding form). This entails storing entries of
;;; the form (symbol context*) in the database and augmenting
;;; record-callers to keep the context around. The only drawbacks is
;;; that it would cons a fair bit. If we do this, we should store
;;; additional information as well in the database, such as the caller
;;; pattern type (e.g., variable vs. function).
;;;
;;; Write a translator from BNF (at least as much of BNF as is used
;;; in CLtL2), to the format used here.
;;;
;;; Should automatically add new patterns for new functions and macros
;;; based on their arglists. Probably requires much more than this
;;; simple code walker, so there isn't much we can do.
;;;
;;; Defmacro is a problem, because it often hides internal function
;;; calls within backquote and quote, which we normally ignore. If
;;; we redefine QUOTE's pattern so that it treats the arg like a FORM,
;;; we'll probably get them (though maybe the syntax will be mangled),
;;; but most likely a lot of spurious things as well.
;;;
;;; Define an operation for Defsystem which will run XREF-FILE on the
;;; files of the system. Or yet simpler, when XREF sees a LOAD form
;;; for which the argument is a string, tries to recursively call
;;; XREF-FILE on the specified file. Then one could just XREF-FILE
;;; the file which loads the system. (This should be a program
;;; parameter.)
;;;
;;; Have special keywords which the user may place in a file to have
;;; XREF-FILE ignore a region.
;;;
;;; Should we distinguish flet and labels from defun? I.e., note that
;;; flet's definitions are locally defined, instead of just lumping
;;; them in with regular definitions.
;;;
;;; Add patterns for series, loop macro.
;;;
;;; Need to integrate the variable reference database with the other
;;; databases, yet maintain separation. So we can distinguish all
;;; the different types of variable and function references, without
;;; multiplying databases.
;;;
;;; Would pay to comment record-callers and record-callers* in more
;;; depth.
;;;
;;; (&OPTIONAL &REST &KEY &AUX &BODY &WHOLE &ALLOW-OTHER-KEYS &ENVIRONMENT)
;;; ********************************
;;; Notes **************************
;;; ********************************
;;;
;;; XREF has been tested (successfully) in the following lisps:
;;; CMU Common Lisp (M2.9 15-Aug-90, Compiler M1.8 15-Aug-90)
;;; Macintosh Allegro Common Lisp (1.3.2)
;;; ExCL (Franz Allegro CL 3.1.12 [DEC 3100] 3/30/90)
;;; Lucid CL (Version 2.1 6-DEC-87)
;;;
;;; XREF has been tested (unsuccessfully) in the following lisps:
;;; Ibuki Common Lisp (01/01, October 15, 1987)
;;; - if interpreted, runs into stack overflow
;;; - does not compile (tried ibcl on Suns, PMAXes and RTs)
;;; seems to be due to a limitation in the c compiler.
;;;
;;; XREF needs to be tested in the following lisps:
;;; Symbolics Common Lisp (8.0)
;;; Lucid Common Lisp (3.0, 4.0)
;;; KCL (June 3, 1987 or later)
;;; AKCL (1.86, June 30, 1987 or later)
;;; TI (Release 4.1 or later)
;;; Golden Common Lisp (3.1 IBM-PC)
;;; VAXLisp (2.0, 3.1)
;;; HP Common Lisp (same as Lucid?)
;;; Procyon Common Lisp
;;; ****************************************************************
;;; Documentation **************************************************
;;; ****************************************************************
;;;
;;; XREF analyzes a user's program, determining which functions call a
;;; given function, and the location of where variables are bound/assigned
;;; and used. The user may retrieve this information for either a single
;;; symbol, or display the call graph of portions of the program
;;; (including the entire program). This allows the programmer to debug
;;; and document the program's structure.
;;;
;;; XREF is primarily intended for analyzing large programs, where it is
;;; difficult, if not impossible, for the programmer to grasp the structure
;;; of the whole program. Nothing precludes using XREF for smaller programs,
;;; where it can be useful for inspecting the relationships between pieces
;;; of the program and for documenting the program.
;;;
;;; Two aspects of the Lisp programming language greatly simplify the
;;; analysis of Lisp programs:
;;; o Lisp programs are naturally represented as data.
;;; Successive definitions from a file are easily read in
;;; as list structure.
;;; o The basic syntax of Lisp is uniform. A list program
;;; consists of a set of nested forms, where each form is
;;; a list whose car is a tag (e.g., function name) that
;;; specifies the structure of the rest of the form.
;;; Thus Lisp programs, when represented as data, can be considered to be
;;; parse trees. Given a grammar of syntax patterns for the language, XREF
;;; recursively descends the parse tree for a given definition, computing
;;; a set of relations that hold for the definition at each node in the
;;; tree. For example, one kind of relation is that the function defined
;;; by the definition calls the functions in its body. The relations are
;;; stored in a database for later examination by the user.
;;;
;;; While XREF currently only works for programs written in Lisp, it could
;;; be extended to other programming languages by writing a function to
;;; generate parse trees for definitions in that language, and a core
;;; set of patterns for the language's syntax.
;;;
;;; Since XREF normally does a static syntactic analysis of the program,
;;; it does not detect references due to the expansion of a macro definition.
;;; To do this in full generality XREF would have to have knowledge about the
;;; semantics of the program (e.g., macros which call other functions to
;;; do the expansion). This entails either modifying the compiler to
;;; record the relationships (e.g., Symbolics Who-Calls Database) or doing
;;; a walk of loaded code and macroexpanding as needed (PCL code walker).
;;; The former is not portable, while the latter requires that the code
;;; used by macros be loaded and in working order. On the other hand, then
;;; we would need no special knowledge about macros (excluding the 24 special
;;; forms of Lisp).
;;;
;;; Parameters may be set to enable macro expansion in XREF. Then XREF
;;; will expand any macros for which it does not have predefined patterns.
;;; (For example, most Lisps will implement dolist as a macro. Since XREF
;;; has a pattern defined for dolist, it will not call macroexpand-1 on
;;; a form whose car is dolist.) For this to work properly, the code must
;;; be loaded before being processed by XREF, and XREF's parameters should
;;; be set so that it processes forms in their proper packages.
;;;
;;; If macro expansion is disabled, the default rules for handling macro
;;; references may not be sufficient for some user-defined macros, because
;;; macros allow a variety of non-standard syntactic extensions to the
;;; language. In this case, the user may specify additional templates in
;;; a manner similar to that in which the core Lisp grammar was specified.
;;;
;;; ********************************
;;; User Guide *********************
;;; ********************************
;;; -----
;;; The following functions are called to cross reference the source files.
;;;
;;; XREF-FILES (&rest files) [FUNCTION]
;;; Grovels over the lisp code located in source file FILES, using
;;; xref-file.
;;;
;;; XREF-FILE (filename &optional clear-tables verbose) [Function]
;;; Cross references the function and variable calls in FILENAME by
;;; walking over the source code located in the file. Defaults type of
;;; filename to ".lisp". Chomps on the code using record-callers and
;;; record-callers*. If CLEAR-TABLES is T (the default), it clears the
;;; callers database before processing the file. Specify CLEAR-TABLES as
;;; nil to append to the database. If VERBOSE is T (the default), prints
;;; out the name of the file, one progress dot for each form processed,
;;; and the total number of forms.
;;;
;;; -----
;;; The following functions display information about the uses of the
;;; specified symbol as a function, variable, or constant.
;;;
;;; LIST-CALLERS (symbol) [FUNCTION]
;;; Lists all functions which call SYMBOL as a function (function
;;; invocation).
;;;
;;; LIST-READERS (symbol) [FUNCTION]
;;; Lists all functions which refer to SYMBOL as a variable
;;; (variable reference).
;;;
;;; LIST-SETTERS (symbol) [FUNCTION]
;;; Lists all functions which bind/set SYMBOL as a variable
;;; (variable mutation).
;;;
;;; LIST-USERS (symbol) [FUNCTION]
;;; Lists all functions which use SYMBOL as a variable or function.
;;;
;;; WHO-CALLS (symbol &optional how) [FUNCTION]
;;; Lists callers of symbol. HOW may be :function, :reader, :setter,
;;; or :variable."
;;;
;;; WHAT-FILES-CALL (symbol) [FUNCTION]
;;; Lists names of files that contain uses of SYMBOL
;;; as a function, variable, or constant.
;;;
;;; SOURCE-FILE (symbol) [FUNCTION]
;;; Lists the names of files in which SYMBOL is defined/used.
;;;
;;; LIST-CALLEES (symbol) [FUNCTION]
;;; Lists names of functions and variables called by SYMBOL.
;;;
;;; -----
;;; The following functions may be useful for viewing the database and
;;; debugging the calling patterns.
;;;
;;; *LAST-FORM* () [VARIABLE]
;;; The last form read from the file. Useful for figuring out what went
;;; wrong when xref-file drops into the debugger.
;;;
;;; *XREF-VERBOSE* t [VARIABLE]
;;; When T, xref-file(s) prints out the names of the files it looks at,
;;; progress dots, and the number of forms read.
;;;
;;; *TYPES-TO-IGNORE* (quote (:lisp :lisp2)) [VARIABLE]
;;; Default set of caller types (as specified in the patterns) to ignore
;;; in the database handling functions. :lisp is CLtL 1st edition,
;;; :lisp2 is additional patterns from CLtL 2nd edition.
;;;
;;; *HANDLE-PACKAGE-FORMS* () [VARIABLE]
;;; When non-NIL, and XREF-FILE sees a package-setting form like
;;; IN-PACKAGE, sets the current package to the specified package by
;;; evaluating the form. When done with the file, xref-file resets the
;;; package to its original value. In some of the displaying functions,
;;; when this variable is non-NIL one may specify that all symbols from a
;;; particular set of packages be ignored. This is only useful if the
;;; files use different packages with conflicting names.
;;;
;;; *HANDLE-FUNCTION-FORMS* t [VARIABLE]
;;; When T, XREF-FILE tries to be smart about forms which occur in
;;; a function position, such as lambdas and arbitrary Lisp forms.
;;; If so, it recursively calls record-callers with pattern 'FORM.
;;; If the form is a lambda, makes the caller a caller of
;;; :unnamed-lambda.
;;;
;;; *HANDLE-MACRO-FORMS* t [VARIABLE]
;;; When T, if the file was loaded before being processed by XREF, and
;;; the car of a form is a macro, it notes that the parent calls the
;;; macro, and then calls macroexpand-1 on the form.
;;;
;;; *DEFAULT-GRAPHING-MODE* :call-graph [VARIABLE]
;;; Specifies whether we graph up or down. If :call-graph, the children
;;; of a node are the functions it calls. If :caller-graph, the
;;; children of a node are the functions that call it.
;;;
;;; *INDENT-AMOUNT* 3 [VARIABLE]
;;; Number of spaces to indent successive levels in PRINT-INDENTED-TREE.
;;;
;;; DISPLAY-DATABASE (&optional database types-to-ignore) [FUNCTION]
;;; Prints out the name of each symbol and all its callers. Specify
;;; database :callers (the default) to get function call references,
;;; :file to the get files in which the symbol is called, :readers to get
;;; variable references, and :setters to get variable binding and
;;; assignments. Ignores functions of types listed in types-to-ignore.
;;;
;;; PRINT-CALLER-TREES (&key (mode *default-graphing-mode*) [FUNCTION]
;;; (types-to-ignore *types-to-ignore*)
;;; compact root-nodes)
;;; Prints the calling trees (which may actually be a full graph and not
;;; necessarily a DAG) as indented text trees using
;;; PRINT-INDENTED-TREE. MODE is :call-graph for trees where the children
;;; of a node are the functions called by the node, or :caller-graph for
;;; trees where the children of a node are the functions the node calls.
;;; TYPES-TO-IGNORE is a list of funcall types (as specified in the
;;; patterns) to ignore in printing out the database. For example,
;;; '(:lisp) would ignore all calls to common lisp functions. COMPACT is
;;; a flag to tell the program to try to compact the trees a bit by not
;;; printing trees if they have already been seen. ROOT-NODES is a list
;;; of root nodes of trees to display. If ROOT-NODES is nil, tries to
;;; find all root nodes in the database.
;;;
;;; MAKE-CALLER-TREE (&optional (mode *default-graphing-mode*) [FUNCTION]
;;; (types-to-ignore *types-to-ignore*)
;;; compact)
;;; Outputs list structure of a tree which roughly represents the
;;; possibly cyclical structure of the caller database.
;;; If mode is :call-graph, the children of a node are the functions
;;; it calls. If mode is :caller-graph, the children of a node are the
;;; functions that call it.
;;; If compact is T, tries to eliminate the already-seen nodes, so
;;; that the graph for a node is printed at most once. Otherwise it will
;;; duplicate the node's tree (except for cycles). This is usefull
;;; because the call tree is actually a directed graph, so we can either
;;; duplicate references or display only the first one.
;;;
;;; DETERMINE-FILE-DEPENDENCIES (&optional database) [FUNCTION]
;;; Makes a hash table of file dependencies for the references listed in
;;; DATABASE. This function may be useful for automatically resolving
;;; file references for automatic creation of a system definition
;;; (defsystem).
;;;
;;; PRINT-FILE-DEPENDENCIES (&optional database) [FUNCTION]
;;; Prints a list of file dependencies for the references listed in
;;; DATABASE. This function may be useful for automatically computing
;;; file loading constraints for a system definition tool.
;;;
;;; WRITE-CALLERS-DATABASE-TO-FILE (filename) [FUNCTION]
;;; Saves the contents of the current callers database to a file. This
;;; file can be loaded to restore the previous contents of the
;;; database. (For large systems it can take a long time to crunch
;;; through the code, so this can save some time.)
;;;
;;; -----
;;; The following macros define new function and macro call patterns.
;;; They may be used to extend the static analysis tool to handle
;;; new def forms, extensions to Common Lisp, and program defs.
;;;
;;; DEFINE-PATTERN-SUBSTITUTION (name pattern) [MACRO]
;;; Defines NAME to be equivalent to the specified pattern. Useful for
;;; making patterns more readable. For example, the LAMBDA-LIST is
;;; defined as a pattern substitution, making the definition of the
;;; DEFUN caller-pattern simpler.
;;;
;;; DEFINE-CALLER-PATTERN (name pattern &optional caller-type) [MACRO]
;;; Defines NAME as a function/macro call with argument structure
;;; described by PATTERN. CALLER-TYPE, if specified, assigns a type to
;;; the pattern, which may be used to exclude references to NAME while
;;; viewing the database. For example, all the Common Lisp definitions
;;; have a caller-type of :lisp or :lisp2, so that you can exclude
;;; references to common lisp functions from the calling tree.
;;;
;;; DEFINE-VARIABLE-PATTERN (name &optional caller-type) [MACRO]
;;; Defines NAME as a variable reference of type CALLER-TYPE. This is
;;; mainly used to establish the caller-type of the variable.
;;;
;;; DEFINE-CALLER-PATTERN-SYNONYMS (source destinations) [MACRO]
;;; For defining function caller pattern syntax synonyms. For each name
;;; in DESTINATIONS, defines its pattern as a copy of the definition
;;; of SOURCE. Allows a large number of identical patterns to be defined
;;; simultaneously. Must occur after the SOURCE has been defined.
;;;
;;; -----
;;; This system includes pattern definitions for the latest
;;; common lisp specification, as published in Guy Steele,
;;; Common Lisp: The Language, 2nd Edition.
;;;
;;; Patterns may be either structures to match, or a predicate
;;; like symbolp/numberp/stringp. The pattern specification language
;;; is similar to the notation used in CLtL2, but in a more lisp-like
;;; form:
;;; (:eq name) The form element must be eq to the symbol NAME.
;;; (:test test) TEST must be true when applied to the form element.
;;; (:typep type) The form element must be of type TYPE.
;;; (:or pat1 pat2 ...) Tries each of the patterns in left-to-right order,
;;; until one succeeds.
;;; Equivalent to { pat1 | pat2 | ... }
;;; (:rest pattern) The remaining form elements are grouped into a
;;; list which is matched against PATTERN.
;;; (:optional pat1 ...) The patterns may optionally match against the
;;; form element.
;;; Equivalent to [ pat1 ... ].
;;; (:star pat1 ...) The patterns may match against the patterns
;;; any number of times, including 0.
;;; Equivalent to { pat1 ... }*.
;;; (:plus pat1 ...) The patterns may match against the patterns
;;; any number of times, but at least once.
;;; Equivalent to { pat1 ... }+.
;;; &optional, &key, Similar in behavior to the corresponding
;;; &rest lambda-list keywords.
;;; FORM A random lisp form. If a cons, assumes the
;;; car is a function or macro and tries to
;;; match the args against that symbol's pattern.
;;; If a symbol, assumes it's a variable reference.
;;; :ignore Ignores the corresponding form element.
;;; NAME The corresponding form element should be
;;; the name of a new definition (e.g., the
;;; first arg in a defun pattern is NAME.
;;; FUNCTION, MACRO The corresponding form element should be
;;; a function reference not handled by FORM.
;;; Used in the definition of apply and funcall.
;;; VAR The corresponding form element should be
;;; a variable definition or mutation. Used
;;; in the definition of let, let*, etc.
;;; VARIABLE The corresponding form element should be
;;; a variable reference.
;;;
;;; In all other pattern symbols, it looks up the symbols pattern substitution
;;; and recursively matches against the pattern. Automatically destructures
;;; list structure that does not include consing dots.
;;;
;;; Among the pattern substitution names defined are:
;;; STRING, SYMBOL, NUMBER Appropriate :test patterns.
;;; LAMBDA-LIST Matches against a lambda list.
;;; BODY Matches against a function body definition.
;;; FN Matches against #'function, 'function,
;;; and lambdas. This is used in the definition
;;; of apply, funcall, and the mapping patterns.
;;; and others...
;;;
;;; Here's some sample pattern definitions:
;;; (define-caller-pattern defun
;;; (name lambda-list
;;; (:star (:or documentation-string declaration))
;;; (:star form))
;;; :lisp)
;;; (define-caller-pattern funcall (fn (:star form)) :lisp)
;;;
;;; In general, the system is intelligent enough to handle any sort of
;;; simple funcall. One only need specify the syntax for functions and
;;; macros which use optional arguments, keyword arguments, or some
;;; argument positions are special, such as in apply and funcall, or
;;; to indicate that the function is of the specified caller type.
;;;
;;;
;;; NOTES:
;;;
;;; XRef assumes syntactically correct lisp code.
;;;
;;; This is by no means perfect. For example, let and let* are treated
;;; identically, instead of differentiating between serial and parallel
;;; binding. But it's still a useful tool. It can be helpful in
;;; maintaining code, debugging problems with patch files, determining
;;; whether functions are multiply defined, and help you remember where
;;; a function is defined or called.
;;;
;;; XREF runs best when compiled.
;;; ********************************
;;; References *********************
;;; ********************************
;;;
;;; Xerox Interlisp Masterscope Program:
;;; Larry M Masinter, Global program analysis in an interactive environment
;;; PhD Thesis, Stanford University, 1980.
;;;
;;; Symbolics Who-Calls Database:
;;; User's Guide to Symbolics Computers, Volume 1, Cambridge, MA, July 1986
;;; Genera 7.0, pp 183-185.
;;;
;;; ********************************
;;; Example ************************
;;; ********************************
;;;
;;; Here is an example of running XREF on a short program.
;;; [In Scribe documentation, give a simple short program and resulting
;;; XREF output, including postscript call graphs.]
#|
<cl> (xref:xref-file "/afs/cs/user/mkant/Lisp/Graph-Dag/graph-dag.lisp")
Cross-referencing file /afs/cs/user/mkant/Lisp/Graph-Dag/graph-dag.lisp.
................................................
48 forms processed.
<cl> (xref:display-database :readers)
*DISPLAY-CUTOFF-DEPTH* is referenced by CALCULATE-LEVEL-POSITION CALCULATE-LEVEL-POSITION-BEFORE CALCULATE-POSITION-IN-LEVEL.
*OFFSET-FROM-EDGE-OF-PANE* is referenced by CALCULATE-LEVEL-POSITION CALCULATE-LEVEL-POSITION-BEFORE.
*WITHIN-LEVEL-SPACING* is referenced by BREADTH CALCULATE-POSITION-INFO.
*DIRECTION* is referenced by CREATE-POSITION-INFO.
*LINK-OFFSET* is referenced by OFFSET-OF-LINK-FROM-ATTACHMENT-POINT.
*ROOT-IS-SEQUENCE* is referenced by GRAPH.
*LEVEL-SPACING* is referenced by CALCULATE-LEVEL-POSITION CALCULATE-LEVEL-POSITION-BEFORE.
*ORIENTATION* is referenced by BREADTH CALCULATE-LEVEL-POSITION CALCULATE-LEVEL-POSITION-BEFORE CALCULATE-POSITION-IN-LEVEL.
*DEFAULT-GRAPH-POSITION* is referenced by CREATE-POSITION-INFO.
*GRAPHING-CUTOFF-DEPTH* is referenced by CREATE-NODE-STRUCTURE.
*LIST-OF-NODES* is referenced by CALCULATE-LEVEL-POSITION CALCULATE-LEVEL-POSITION-BEFORE CREATE-NODE FIND-NODE.
*GRAPH-TYPE* is referenced by CREATE-NODE-STRUCTURE.
<cl> (xref:print-caller-trees :root-nodes '(display-graph))
Rooted calling trees:
DISPLAY-GRAPH
CREATE-POSITION-INFO
CALCULATE-POSITION-INFO
CALCULATE-POSITION
NODE-POSITION-ALREADY-SET-FLAG
NODE-LEVEL-ALREADY-SET-FLAG
CALCULATE-POSITION-IN-LEVEL
NODE-CHILDREN
NODE-LEVEL
CALCULATE-POSITION
NEW-CALCULATE-BREADTH
NODE-CHILDREN
BREADTH
OPPOSITE-DIMENSION
NODE-HEIGHT
NODE-WIDTH
NEW-CALCULATE-BREADTH
NODE-PARENTS
OPPOSITE-DIMENSION
NODE-HEIGHT
NODE-WIDTH
OPPOSITE-POSITION
NODE-Y
NODE-X
NODE-LEVEL
CALCULATE-LEVEL-POSITION
NODE-LEVEL
NODE-POSITION
NODE-X
NODE-Y
DIMENSION
NODE-WIDTH
NODE-HEIGHT
CALCULATE-LEVEL-POSITION-BEFORE
NODE-LEVEL
NODE-POSITION
NODE-X
NODE-Y
NODE-WIDTH
NODE-HEIGHT
DIMENSION
NODE-WIDTH
NODE-HEIGHT
|#
;;; ****************************************************************
;;; List Callers ***************************************************
;;; ****************************************************************
(defpackage :pxref
(:use :common-lisp)
(:export #:list-callers
#:list-users
#:list-readers
#:list-setters
#:what-files-call
#:who-calls
#:list-callees
#:source-file
#:clear-tables
#:define-pattern-substitution
#:define-caller-pattern
#:define-variable-pattern
#:define-caller-pattern-synonyms
#:clear-patterns
#:*last-form*
#:*xref-verbose*
#:*handle-package-forms*
#:*handle-function-forms*
#:*handle-macro-forms*
#:*types-to-ignore*
#:*last-caller-tree*
#:*default-graphing-mode*
#:*indent-amount*
#:xref-file
#:xref-files
#:write-callers-database-to-file
#:display-database
#:print-caller-trees
#:make-caller-tree
#:print-indented-tree
#:determine-file-dependencies
#:print-file-dependencies
#:psgraph-xref
))
(in-package "PXREF")
;;; Warn user if they're loading the source instead of compiling it first.
;(eval-when (compile load eval)
; (defvar compiled-p nil))
;(eval-when (compile load)
; (setq compiled-p t))
;(eval-when (load eval)
; (unless compiled-p
; (warn "This file should be compiled before loading for best results.")))
(eval-when (eval)
(warn "This file should be compiled before loading for best results."))
;;; ********************************
;;; Primitives *********************
;;; ********************************
(defun lookup (symbol environment)
(dolist (frame environment)
(when (member symbol frame)
(return symbol))))
(defun car-eq (list item)
(and (consp list)
(eq (car list) item)))
;;; ********************************
;;; Callers Database ***************
;;; ********************************
(defvar *file-callers-database* (make-hash-table :test #'equal)
"Contains name and list of file callers (files which call) for that name.")
(defvar *callers-database* (make-hash-table :test #'equal)
"Contains name and list of callers (function invocation) for that name.")
(defvar *readers-database* (make-hash-table :test #'equal)
"Contains name and list of readers (variable use) for that name.")
(defvar *setters-database* (make-hash-table :test #'equal)
"Contains name and list of setters (variable mutation) for that name.")
(defvar *callees-database* (make-hash-table :test #'equal)
"Contains name and list of functions and variables it calls.")
(defun callers-list (name &optional (database :callers))
(case database
(:file (gethash name *file-callers-database*))
(:callees (gethash name *callees-database*))
(:callers (gethash name *callers-database*))
(:readers (gethash name *readers-database*))
(:setters (gethash name *setters-database*))))
(defsetf callers-list (name &optional (database :callers)) (caller)
`(setf (gethash ,name (case ,database
(:file *file-callers-database*)
(:callees *callees-database*)
(:callers *callers-database*)
(:readers *readers-database*)
(:setters *setters-database*)))
,caller))
(defun list-callers (symbol)
"Lists all functions which call SYMBOL as a function (function invocation)."
(callers-list symbol :callers))
(defun list-readers (symbol)
"Lists all functions which refer to SYMBOL as a variable
(variable reference)."
(callers-list symbol :readers))
(defun list-setters (symbol)
"Lists all functions which bind/set SYMBOL as a variable
(variable mutation)."
(callers-list symbol :setters))
(defun list-users (symbol)
"Lists all functions which use SYMBOL as a variable or function."
(values (list-callers symbol)
(list-readers symbol)
(list-setters symbol)))
(defun who-calls (symbol &optional how)
"Lists callers of symbol. HOW may be :function, :reader, :setter,
or :variable."
;; would be nice to have :macro and distinguish variable
;; binding from assignment. (i.e., variable binding, assignment, and use)
(case how
(:function (list-callers symbol))
(:reader (list-readers symbol))
(:setter (list-setters symbol))
(:variable (append (list-readers symbol)
(list-setters symbol)))
(otherwise (append (list-callers symbol)
(list-readers symbol)
(list-setters symbol)))))
(defun what-files-call (symbol)
"Lists names of files that contain uses of SYMBOL
as a function, variable, or constant."
(callers-list symbol :file))
(defun list-callees (symbol)
"Lists names of functions and variables called by SYMBOL."
(callers-list symbol :callees))
(defvar *source-file* (make-hash-table :test #'equal)
"Contains function name and source file for that name.")
(defun source-file (symbol)
"Lists the names of files in which SYMBOL is defined/used."
(gethash symbol *source-file*))
(defsetf source-file (name) (value)
`(setf (gethash ,name *source-file*) ,value))
(defun clear-tables ()
(clrhash *file-callers-database*)
(clrhash *callers-database*)
(clrhash *callees-database*)
(clrhash *readers-database*)
(clrhash *setters-database*)
(clrhash *source-file*))
;;; ********************************
;;; Pattern Database ***************
;;; ********************************
;;; Pattern Types
(defvar *pattern-caller-type* (make-hash-table :test #'equal))
(defun pattern-caller-type (name)
(gethash name *pattern-caller-type*))
(defsetf pattern-caller-type (name) (value)
`(setf (gethash ,name *pattern-caller-type*) ,value))
;;; Pattern Substitutions
(defvar *pattern-substitution-table* (make-hash-table :test #'equal)
"Stores general patterns for function destructuring.")
(defun lookup-pattern-substitution (name)
(gethash name *pattern-substitution-table*))
(defmacro define-pattern-substitution (name pattern)
"Defines NAME to be equivalent to the specified pattern. Useful for
making patterns more readable. For example, the LAMBDA-LIST is
defined as a pattern substitution, making the definition of the
DEFUN caller-pattern simpler."
`(setf (gethash ',name *pattern-substitution-table*)
',pattern))
;;; Function/Macro caller patterns:
;;; The car of the form is skipped, so we don't need to specify
;;; (:eq function-name) like we would for a substitution.
;;;
;;; Patterns must be defined in the XREF package because the pattern
;;; language is tested by comparing symbols (using #'equal) and not
;;; their printreps. This is fine for the lisp grammer, because the XREF
;;; package depends on the LISP package, so a symbol like 'xref::cons is
;;; translated automatically into 'lisp::cons. However, since
;;; (equal 'foo::bar 'baz::bar) returns nil unless both 'foo::bar and
;;; 'baz::bar are inherited from the same package (e.g., LISP),
;;; if package handling is turned on the user must specify package
;;; names in the caller pattern definitions for functions that occur
;;; in packages other than LISP, otherwise the symbols will not match.
;;;
;;; Perhaps we should enforce the definition of caller patterns in the
;;; XREF package by wrapping the body of define-caller-pattern in
;;; the XREF package:
;;; (defmacro define-caller-pattern (name value &optional caller-type)
;;; (let ((old-package *package*))
;;; (setf *package* (find-package "XREF"))
;;; (prog1
;;; `(progn
;;; (when ',caller-type
;;; (setf (pattern-caller-type ',name) ',caller-type))
;;; (when ',value
;;; (setf (gethash ',name *caller-pattern-table*)
;;; ',value)))
;;; (setf *package* old-package))))
;;; Either that, or for the purpose of pattern testing we should compare
;;; printreps. [The latter makes the primitive patterns like VAR
;;; reserved words.]
(defvar *caller-pattern-table* (make-hash-table :test #'equal)
"Stores patterns for function destructuring.")
(defun lookup-caller-pattern (name)
(gethash name *caller-pattern-table*))
(defmacro define-caller-pattern (name pattern &optional caller-type)
"Defines NAME as a function/macro call with argument structure
described by PATTERN. CALLER-TYPE, if specified, assigns a type to
the pattern, which may be used to exclude references to NAME while
viewing the database. For example, all the Common Lisp definitions
have a caller-type of :lisp or :lisp2, so that you can exclude
references to common lisp functions from the calling tree."
`(progn
(when ',caller-type
(setf (pattern-caller-type ',name) ',caller-type))
(when ',pattern
(setf (gethash ',name *caller-pattern-table*)
',pattern))))
;;; For defining variables
(defmacro define-variable-pattern (name &optional caller-type)
"Defines NAME as a variable reference of type CALLER-TYPE. This is
mainly used to establish the caller-type of the variable."
`(progn
(when ',caller-type
(setf (pattern-caller-type ',name) ',caller-type))))
;;; For defining synonyms. Means much less space taken up by the patterns.
(defmacro define-caller-pattern-synonyms (source destinations)
"For defining function caller pattern syntax synonyms. For each name
in DESTINATIONS, defines its pattern as a copy of the definition of SOURCE.
Allows a large number of identical patterns to be defined simultaneously.
Must occur after the SOURCE has been defined."
`(let ((source-type (pattern-caller-type ',source))
(source-pattern (gethash ',source *caller-pattern-table*)))
(when source-type
(dolist (dest ',destinations)
(setf (pattern-caller-type dest) source-type)))
(when source-pattern
(dolist (dest ',destinations)
(setf (gethash dest *caller-pattern-table*)
source-pattern)))))
(defun clear-patterns ()
(clrhash *pattern-substitution-table*)
(clrhash *caller-pattern-table*)
(clrhash *pattern-caller-type*))
;;; ********************************
;;; Cross Reference Files **********
;;; ********************************
(defvar *last-form* ()
"The last form read from the file. Useful for figuring out what went wrong
when xref-file drops into the debugger.")
(defvar *xref-verbose* t
"When T, xref-file(s) prints out the names of the files it looks at,
progress dots, and the number of forms read.")
;;; This needs to first clear the tables?
(defun xref-files (&rest files)
"Grovels over the lisp code located in source file FILES, using xref-file."
;; If the arg is a list, use it.
(when (listp (car files)) (setq files (car files)))
(dolist (file files)
(xref-file file nil))
(values))
(defvar *handle-package-forms* nil ;'(lisp::in-package)
"When non-NIL, and XREF-FILE sees a package-setting form like IN-PACKAGE,
sets the current package to the specified package by evaluating the
form. When done with the file, xref-file resets the package to its
original value. In some of the displaying functions, when this variable
is non-NIL one may specify that all symbols from a particular set of
packages be ignored. This is only useful if the files use different
packages with conflicting names.")
(defvar *normal-readtable* (copy-readtable nil)
"Normal, unadulterated CL readtable.")
(defun xref-file (filename &optional (clear-tables t) (verbose *xref-verbose*))
"Cross references the function and variable calls in FILENAME by
walking over the source code located in the file. Defaults type of
filename to \".lisp\". Chomps on the code using record-callers and
record-callers*. If CLEAR-TABLES is T (the default), it clears the callers
database before processing the file. Specify CLEAR-TABLES as nil to
append to the database. If VERBOSE is T (the default), prints out the
name of the file, one progress dot for each form processed, and the
total number of forms."
;; Default type to "lisp"
(when (and (null (pathname-type filename))
(not (probe-file filename)))
(cond ((stringp filename)
(setf filename (concatenate 'string filename ".lisp")))
((pathnamep filename)
(setf filename (merge-pathnames filename
(make-pathname :type "lisp"))))))
(when clear-tables (clear-tables))
(let ((count 0)
(old-package *package*)
(*readtable* *normal-readtable*))
(when verbose
(format t "~&Cross-referencing file ~A.~&" filename))
(with-open-file (stream filename :direction :input)
(do ((form (read stream nil :eof) (read stream nil :eof)))
((eq form :eof))
(incf count)
(when verbose
(format *standard-output* ".")
(force-output *standard-output*))
(setq *last-form* form)
(record-callers filename form)
;; Package Magic.
(when (and *handle-package-forms*
(consp form)
(member (car form) *handle-package-forms*))
(eval form))))
(when verbose
(format t "~&~D forms processed." count))
(setq *package* old-package)
(values)))
(defvar *handle-function-forms* t
"When T, XREF-FILE tries to be smart about forms which occur in
a function position, such as lambdas and arbitrary Lisp forms.
If so, it recursively calls record-callers with pattern 'FORM.
If the form is a lambda, makes the caller a caller of :unnamed-lambda.")
(defvar *handle-macro-forms* t
"When T, if the file was loaded before being processed by XREF, and the
car of a form is a macro, it notes that the parent calls the macro,
and then calls macroexpand-1 on the form.")
(defvar *callees-database-includes-variables* nil)
(defun record-callers (filename form
&optional pattern parent (environment nil)
funcall)
"RECORD-CALLERS is the main routine used to walk down the code. It matches
the PATTERN against the FORM, possibly adding statements to the database.
PARENT is the name defined by the current outermost definition; it is
the caller of the forms in the body (e.g., FORM). ENVIRONMENT is used
to keep track of the scoping of variables. FUNCALL deals with the type
of variable assignment and hence how the environment should be modified.