0.6.11.34:
[sbcl.git] / src / code / inspect.lisp
1 ;;;; the INSPECT function
2
3 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; more information.
5 ;;;;
6 ;;;; This software is derived from the CMU CL system, which was
7 ;;;; written at Carnegie Mellon University and released into the
8 ;;;; public domain. The software is in the public domain and is
9 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
10 ;;;; files for more information.
11
12 (in-package "SB-IMPL")
13
14 ;;; The inspector views LISP objects as being composed of parts. A
15 ;;; list, for example, would be divided into its members, and a
16 ;;; instance into its slots. These parts are stored in a list. The
17 ;;; first two elements of this list are for bookkeeping. The first
18 ;;; element is a preamble string that will be displayed before the
19 ;;; object. The second element is a boolean value that indicates
20 ;;; whether a label will be printed in front of a value, or just the
21 ;;; value. Symbols and instances need to display both a slot name and
22 ;;; a value, while lists, vectors, and atoms need only display a
23 ;;; value. If the second member of a parts list is t, then the third
24 ;;; and successive members must be an association list of slot names
25 ;;; and values. When the second slot is nil, the third and successive
26 ;;; slots must be the parts of an object.
27
28 ;;; *INSPECT-OBJECT-STACK* is an assoc list of objects to their parts.
29 (defvar *inspect-object-stack* ())
30
31 (defparameter *inspect-length* 10)
32
33 #-sb-fluid (declaim (inline numbered-parts-p))
34 (defun numbered-parts-p (parts)
35   (second parts))
36
37 (defconstant parts-offset 2)
38
39 (defun nth-parts (parts n)
40   (if (numbered-parts-p parts)
41       (cdr (nth (+ n parts-offset) parts))
42       (nth (+ n parts-offset) parts)))
43
44 (defun inspect (object)
45   (unwind-protect
46       (input-loop object (describe-parts object) *standard-output*)
47     (setf *inspect-object-stack* nil)))
48
49 ;;; When *ILLEGAL-OBJECT-MARKER* occurs in a parts list, it indicates
50 ;;; that that slot is unbound.
51 (defvar *illegal-object-marker* (cons nil nil))
52
53 (defun input-loop (object parts s)
54   (tty-display-object parts s)
55   (loop
56     (format s "~&> ")
57     (force-output)
58     (let ((command (read))
59           ;; Use 2 less than the length because the first 2 elements
60           ;; are bookkeeping.
61           (parts-len-2 (- (length parts) 2)))
62       (typecase command
63         (integer
64          (cond ((< -1 command parts-len-2)
65                 (cond ((eq (nth-parts parts command) *illegal-object-marker*)
66                        (format s "~%That slot is unbound.~%"))
67                       (t
68                        (push (cons object parts) *inspect-object-stack*)
69                        (setf object (nth-parts parts command))
70                        (setf parts (describe-parts object))
71                        (tty-display-object parts s))))
72                (t
73                 (if (= parts-len-2 0)
74                     (format s "~%This object contains nothing to inspect.~%~%")
75                     (format s "~%Enter a VALID number (~:[0-~D~;0~]).~%~%"
76                             (= parts-len-2 1) (1- parts-len-2))))))
77         (symbol
78          (case (find-symbol (symbol-name command) *keyword-package*)
79            ((:q :e)
80             (return object))
81            (:u
82             (cond (*inspect-object-stack*
83                    (setf object (caar *inspect-object-stack*))
84                    (setf parts (cdar *inspect-object-stack*))
85                    (pop *inspect-object-stack*)
86                    (tty-display-object parts s))
87                   (t (format s "~%Bottom of Stack.~%"))))
88            (:r
89             (setf parts (describe-parts object))
90             (tty-display-object parts s))
91            (:d
92             (tty-display-object parts s))
93            ((:h :? :help)
94             (show-help s))
95            (t
96             (do-inspect-eval command s))))
97         (t
98          (do-inspect-eval command s))))))
99
100 (defun do-inspect-eval (command stream)
101   (let ((result-list (restart-case (multiple-value-list (eval command))
102                        (nil () :report "Return to the inspector."
103                           (format stream "~%returning to the inspector~%")
104                           (return-from do-inspect-eval nil)))))
105     (setf /// // // / / result-list)
106     (setf +++ ++ ++ + + - - command)
107     (setf *** ** ** * * (car /))
108     (format stream "~&~{~S~%~}" /)))
109
110 (defun show-help (s)
111   (terpri)
112   (write-line "inspector help:" s)
113   (write-line "  R           -  recompute current object." s)
114   (write-line "  D           -  redisplay current object." s)
115   (write-line "  U           -  Move upward through the object stack." s)
116   (write-line "  Q, E        -  Quit inspector." s)
117   (write-line "  ?, H, Help  -  Show this help." s))
118
119 (defun tty-display-object (parts stream)
120   (format stream "~%~A" (car parts))
121   (let ((numbered-parts-p (numbered-parts-p parts))
122         (parts (cddr parts)))
123     (do ((part parts (cdr part))
124          (i 0 (1+ i)))
125         ((endp part) nil)
126       (if numbered-parts-p
127           (format stream "~D. ~A: ~A~%" i (caar part)
128                   (if (eq (cdar part) *illegal-object-marker*)
129                       "unbound"
130                       (cdar part)))
131           (format stream "~D. ~A~%" i (car part))))))
132 \f
133 ;;;; DESCRIBE-PARTS
134
135 (defun describe-parts (object)
136   (typecase object
137     (symbol (describe-symbol-parts object))
138     (instance (describe-instance-parts object :structure))
139     (function
140      (if (sb-kernel:funcallable-instance-p object)
141          (describe-instance-parts object :funcallable-instance)
142          (describe-function-parts object)))
143     (vector (describe-vector-parts object))
144     (array (describe-array-parts object))
145     (cons (describe-cons-parts object))
146     (t (describe-atomic-parts object))))
147
148 (defun describe-symbol-parts (object)
149   (list (format nil "~S is a symbol.~%" object) t
150         (cons "Value" (if (boundp object)
151                           (symbol-value object)
152                           *illegal-object-marker*))
153         (cons "Function" (if (fboundp object)
154                              (symbol-function object)
155                              *illegal-object-marker*))
156         (cons "Plist" (symbol-plist object))
157         (cons "Package" (symbol-package object))))
158
159 (defun describe-instance-parts (object kind)
160   (let ((info (layout-info (sb-kernel:layout-of object)))
161         (parts-list ()))
162     (push (format nil "~S is a ~(~A~).~%" object kind) parts-list)
163     (push t parts-list)
164     (when (sb-kernel::defstruct-description-p info)
165       (dolist (dd-slot (dd-slots info) (nreverse parts-list))
166         (push (cons (dsd-%name dd-slot)
167                     (funcall (dsd-accessor dd-slot) object))
168               parts-list)))))
169
170 (defun describe-function-parts (object)
171   (let* ((type (sb-kernel:get-type object))
172          (object (if (= type sb-vm:closure-header-type)
173                      (sb-kernel:%closure-function object)
174                      object)))
175     (list (format nil "Function ~S.~@[~%Argument List: ~A~]." object
176                   (sb-kernel:%function-arglist object)
177                   ;; Defined-from stuff used to be here. Someone took
178                   ;; it out. FIXME: We should make it easy to get
179                   ;; to DESCRIBE from the inspector.
180                   )
181           t)))
182
183 (defun describe-vector-parts (object)
184   (list* (format nil "The object is a ~:[~;displaced ~]vector of length ~D.~%"
185                  (and (array-header-p object)
186                       (%array-displaced-p object))
187                  (length object))
188          nil
189          (coerce object 'list)))
190
191 (defun describe-cons-parts (object)
192   (list* (format nil "The object is a LIST of length ~D.~%" (length object))
193          nil
194          object))
195
196 (defun index-string (index rev-dimensions)
197   (if (null rev-dimensions)
198       "[]"
199       (let ((list nil))
200         (dolist (dim rev-dimensions)
201           (multiple-value-bind (q r) (floor index dim)
202             (setq index q)
203             (push r list)))
204         (format nil "[~D~{,~D~}]" (car list) (cdr list)))))
205
206 (defun describe-array-parts (object)
207   (let* ((length (min (array-total-size object) *inspect-length*))
208          (reference-array (make-array length :displaced-to object))
209          (dimensions (array-dimensions object))
210          (parts ()))
211     (push (format nil "The object is ~:[a displaced~;an~] array of ~A.~%~
212                        Its dimensions are ~S.~%"
213                   (array-element-type object)
214                   (and (array-header-p object)
215                        (%array-displaced-p object))
216                   dimensions)
217           parts)
218     (push t parts)
219     (dotimes (i length (nreverse parts))
220       (push (cons (format nil "~A " (index-string i (reverse dimensions)))
221                   (aref reference-array i))
222             parts))))
223
224 (defun describe-atomic-parts (object)
225   (list (format nil "The object is an atom.~%") nil object))