1 ;;;; the CL:INSPECT function
3 ;;;; This software is part of the SBCL system. See the README file for
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.
12 (in-package "SB-IMPL") ;(SB-IMPL, not SB!IMPL, since we're built in warm load.)
14 (declaim #.*optimize-byte-compilation*)
16 (defparameter *inspect-length* 10)
18 ;;; When *INSPECT-UNBOUND-OBJECT-MARKER* occurs in a parts list, it
19 ;;; indicates that that a slot is unbound.
20 (defvar *inspect-unbound-object-marker* (gensym "INSPECT-UNBOUND-OBJECT-"))
22 (defun inspect (object)
23 (declare #.*optimize-external-despite-byte-compilation*)
25 (%inspect object *standard-output*))
29 (setf (documentation '*inspected* 'variable)
30 "the value currently being inspected in CL:INSPECT")
32 (defvar *help-for-inspect*
35 Q, E - Quit the inspector.
36 <integer> - Inspect the numbered slot.
37 R - Redisplay current inspected object.
38 U - Move upward/backward to previous inspected object.
39 ?, H, Help - Show this help.
40 <other> - Evaluate the input as an expression.
41 Within the inspector, the special variable SB-EXT:*INSPECTED* is bound
42 to the current inspected object, so that it can be referred to in
43 evaluated expressions.
46 (defun %inspect (*inspected* s)
47 (named-let redisplay () ; "lambda, the ultimate GOTO":-|
48 (multiple-value-bind (description named-p elements)
49 (inspected-parts *inspected*)
50 (tty-display-inspected-parts description named-p elements s)
54 (let (;; KMP idiom, using stream itself as EOF value
55 (command (read *standard-input* nil *standard-input*)))
58 ;; currently-undocumented feature: EOF is handled as Q.
59 ;; If there's ever consensus that this is *the* right
60 ;; thing to do (as opposed to e.g. handling it as U), we
61 ;; could document it. Meanwhile, it seems more Unix-y to
62 ;; do this than to signal an error.
63 (throw 'quit-inspect nil))
65 (let ((elements-length (length elements)))
66 (cond ((< -1 command elements-length)
67 (let* ((element (nth command elements))
68 (value (if named-p (cdr element) element)))
69 (cond ((eq value *inspect-unbound-object-marker*)
70 (format s "~%That slot is unbound.~%")
71 (return-from %inspect (reread)))
74 ;; If we ever return, then we should be
75 ;; looking at *INSPECTED* again.
76 (return-from %inspect (redisplay))))))
77 ((zerop elements-length)
78 (format s "~%The object contains nothing to inspect.~%")
79 (return-from %inspect (reread)))
81 (format s "~%Enter a valid index (~:[0-~D~;0~]).~%"
82 (= elements-length 1) (1- elements-length))
83 (return-from %inspect (reread))))))
85 (case (find-symbol (symbol-name command) *keyword-package*)
87 (throw 'quit-inspect nil))
89 (return-from %inspect))
91 (return-from %inspect (redisplay)))
93 (write-string *help-for-inspect* s)
94 (return-from %inspect (reread)))
96 (eval-for-inspect command s)
97 (return-from %inspect (reread)))))
99 (eval-for-inspect command s)
100 (return-from %inspect (reread)))))))))
102 (defun eval-for-inspect (command stream)
103 (let ((result-list (restart-case (multiple-value-list (eval command))
104 (nil () :report "Return to the inspector."
105 (format stream "~%returning to the inspector~%")
106 (return-from eval-for-inspect nil)))))
107 ;; FIXME: Much of this interactive-EVAL logic is shared with
108 ;; the main REPL EVAL and with the debugger EVAL. The code should
109 ;; be shared explicitly.
110 (setf /// // // / / result-list)
111 (setf +++ ++ ++ + + - - command)
112 (setf *** ** ** * * (car /))
113 (format stream "~&~{~S~%~}" /)))
115 (defun tty-display-inspected-parts (description named-p elements stream)
116 (format stream "~%~A" description)
118 (dolist (element elements)
120 (destructuring-bind (name . value) element
121 (format stream "~W. ~A: ~W~%" index name
122 (if (eq value *inspect-unbound-object-marker*)
125 (format stream "~W. ~W~%" index element))
130 ;;; Destructure an object for inspection, returning
131 ;;; (VALUES DESCRIPTION NAMED-P ELEMENTS),
134 ;;; DESCRIPTION is a summary description of the destructured object,
135 ;;; e.g. "The object is a CONS.~%".
137 ;;; NAMED-P determines what representation is used for elements
138 ;;; of ELEMENTS. If NAMED-P is true, then each element is
139 ;;; (CONS NAME VALUE); otherwise each element is just VALUE.
141 ;;; ELEMENTS is a list of the component parts of OBJECT (whose
142 ;;; representation is determined by NAMED-P).
144 ;;; (The NAMED-P dichotomy is useful because symbols and instances
145 ;;; need to display both a slot name and a value, while lists and
146 ;;; vectors need only display a value.)
147 (defgeneric inspected-parts (object))
149 (defmethod inspected-parts ((object symbol))
150 (values (format nil "The object is a SYMBOL.~%" object)
152 (list (cons "Name" (symbol-name object))
153 (cons "Package" (symbol-package object))
154 (cons "Value" (if (boundp object)
155 (symbol-value object)
156 *inspect-unbound-object-marker*))
157 (cons "Function" (if (fboundp object)
158 (symbol-function object)
159 *inspect-unbound-object-marker*))
160 (cons "Plist" (symbol-plist object)))))
162 (defun inspected-structure-elements (object)
163 (let ((parts-list '())
164 (info (layout-info (sb-kernel:layout-of object))))
165 (when (sb-kernel::defstruct-description-p info)
166 (dolist (dd-slot (dd-slots info) (nreverse parts-list))
167 (push (cons (dsd-%name dd-slot)
168 (funcall (dsd-accessor dd-slot) object))
171 (defmethod inspected-parts ((object structure-object))
172 (values (format nil "The object is a STRUCTURE-OBJECT of type ~S.~%"
175 (inspected-structure-elements object)))
177 (defun inspected-standard-object-elements (object)
178 (let ((reversed-elements nil)
179 (class-slots (sb-pcl::class-slots (class-of object))))
180 (dolist (class-slot class-slots (nreverse reversed-elements))
181 (let* ((slot-name (slot-value class-slot 'sb-pcl::name))
182 (slot-value (if (slot-boundp object slot-name)
183 (slot-value object slot-name)
184 *inspect-unbound-object-marker*)))
185 (push (cons slot-name slot-value) reversed-elements)))))
187 (defmethod inspected-parts ((object standard-object))
188 (values (format nil "The object is a STANDARD-OBJECT of type ~S.~%"
191 (inspected-standard-object-elements object)))
193 (defmethod inspected-parts ((object funcallable-instance))
194 (values (format nil "The object is a FUNCALLABLE-INSTANCE of type ~S.~%"
197 (inspected-structure-elements object)))
199 (defmethod inspected-parts ((object function))
200 (let* ((type (sb-kernel:get-type object))
201 (object (if (= type sb-vm:closure-header-type)
202 (sb-kernel:%closure-function object)
204 (values (format nil "FUNCTION ~S.~@[~%Argument List: ~A~]." object
205 (sb-kernel:%function-arglist object)
206 ;; Defined-from stuff used to be here. Someone took
207 ;; it out. FIXME: We should make it easy to get
208 ;; to DESCRIBE from the inspector.
213 (defmethod inspected-parts ((object vector))
215 "The object is a ~:[~;displaced ~]VECTOR of length ~D.~%"
216 (and (array-header-p object)
217 (%array-displaced-p object))
220 ;; FIXME: Should we respect *INSPECT-LENGTH* here? If not, what
221 ;; does *INSPECT-LENGTH* mean?
222 (coerce object 'list)))
224 (defun inspected-index-string (index rev-dimensions)
225 (if (null rev-dimensions)
228 (dolist (dim rev-dimensions)
229 (multiple-value-bind (q r) (floor index dim)
232 (format nil "[~D~{,~D~}]" (car list) (cdr list)))))
234 (defmethod inspected-parts ((object array))
235 (let* ((length (min (array-total-size object) *inspect-length*))
236 (reference-array (make-array length :displaced-to object))
237 (dimensions (array-dimensions object))
238 (reversed-elements nil))
239 ;; FIXME: Should we respect *INSPECT-LENGTH* here? If not, what does
240 ;; *INSPECT-LENGTH* mean?
242 (push (cons (format nil
244 (inspected-index-string i (reverse dimensions)))
245 (aref reference-array i))
247 (values (format nil "The object is ~:[a displaced~;an~] ARRAY of ~A.~%~
248 Its dimensions are ~S.~%"
249 (array-element-type object)
250 (and (array-header-p object)
251 (%array-displaced-p object))
254 (nreverse reversed-elements))))
256 (defmethod inspected-parts ((object cons))
257 (if (consp (cdr object))
258 (inspected-parts-of-nontrivial-list object)
259 (inspected-parts-of-simple-cons object)))
261 (defun inspected-parts-of-simple-cons (object)
262 (values "The object is a CONS.
265 (list (cons 'car (car object))
266 (cons 'cdr (cdr object)))))
268 (defun inspected-parts-of-nontrivial-list (object)
271 (reversed-elements nil))
272 (flet ((done (description-format)
273 (return-from inspected-parts-of-nontrivial-list
274 (values (format nil description-format length)
276 (nreverse reversed-elements)))))
278 (cond ((null in-list)
279 (done "The object is a proper list of length ~S.~%"))
280 ((>= length *inspect-length*)
281 (push (cons 'rest in-list) reversed-elements)
282 (done "The object is a long list (more than ~S elements).~%"))
284 (push (cons length (pop in-list)) reversed-elements)
287 (push (cons 'rest in-list) reversed-elements)
288 (done "The object is an improper list of length ~S.~%")))))))
290 (defmethod inspected-parts ((object t))
291 (values (format nil "The object is an ATOM:~% ~W~%" object) nil nil))