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 (defparameter *inspect-length
* 10)
16 ;;; When *INSPECT-UNBOUND-OBJECT-MARKER* occurs in a parts list, it
17 ;;; indicates that that a slot is unbound.
18 (defvar *inspect-unbound-object-marker
* (gensym "INSPECT-UNBOUND-OBJECT-"))
20 (defun inspector (object input-stream output-stream
)
21 (declare (ignore input-stream
))
23 (%inspect object output-stream
))
26 (defvar *inspect-fun
* #'inspector
28 "A function of three arguments OBJECT, INPUT, and OUTPUT which starts an interactive inspector.")
33 (setf (documentation '*inspected
* 'variable
)
34 "the value currently being inspected in CL:INSPECT")
36 (defun inspect (object)
37 (funcall *inspect-fun
* object
*standard-input
* *standard-output
*))
39 (defvar *help-for-inspect
*
42 Q, E - Quit the inspector.
43 <integer> - Inspect the numbered slot.
44 R - Redisplay current inspected object.
45 U - Move upward/backward to previous inspected object.
46 ?, H, Help - Show this help.
47 <other> - Evaluate the input as an expression.
48 Within the inspector, the special variable SB-EXT:*INSPECTED* is bound
49 to the current inspected object, so that it can be referred to in
50 evaluated expressions.
53 (defun %inspect
(*inspected
* s
)
54 (named-let redisplay
() ; "LAMBDA, the ultimate GOTO":-|
55 (multiple-value-bind (description named-p elements
)
56 (inspected-parts *inspected
*)
57 (tty-display-inspected-parts description named-p elements s
)
61 (let* (;; newly-consed object for hermetic protection against
62 ;; mischievous input like #.*EOF-OBJECT*:
63 (eof (cons *eof-object
* nil
))
64 (command (read *standard-input
* nil eof
)))
65 (when (eq command eof
)
66 ;; currently-undocumented feature: EOF is handled as Q.
67 ;; If there's ever consensus that this is *the* right
68 ;; thing to do (as opposed to e.g. handling it as U), we
69 ;; could document it. Meanwhile, it seems more Unix-y to
70 ;; do this than to signal an error.
71 (/show0
"THROWing QUIT-INSPECT for EOF")
72 (throw 'quit-inspect nil
))
75 (let ((elements-length (length elements
)))
76 (cond ((< -
1 command elements-length
)
77 (let* ((element (nth command elements
))
78 (value (if named-p
(cdr element
) element
)))
79 (cond ((eq value
*inspect-unbound-object-marker
*)
80 (format s
"~%That slot is unbound.~%")
81 (return-from %inspect
(reread)))
84 ;; If we ever return, then we should be
85 ;; looking at *INSPECTED* again.
86 (return-from %inspect
(redisplay))))))
87 ((zerop elements-length
)
88 (format s
"~%The object contains nothing to inspect.~%")
89 (return-from %inspect
(reread)))
91 (format s
"~%Enter a valid index (~:[0-~W~;0~]).~%"
92 (= elements-length
1) (1- elements-length
))
93 (return-from %inspect
(reread))))))
95 (case (find-symbol (symbol-name command
) *keyword-package
*)
97 (/show0
"THROWing QUIT-INSPECT for :Q or :E")
98 (throw 'quit-inspect nil
))
100 (return-from %inspect
))
102 (return-from %inspect
(redisplay)))
104 (write-string *help-for-inspect
* s
)
105 (return-from %inspect
(reread)))
107 (eval-for-inspect command s
)
108 (return-from %inspect
(reread)))))
110 (eval-for-inspect command s
)
111 (return-from %inspect
(reread)))))))))
113 (defun eval-for-inspect (command stream
)
114 (let ((result-list (restart-case
115 (multiple-value-list (interactive-eval command
))
116 (nil () :report
"Return to the inspector."
117 (format stream
"~%returning to the inspector~%")
118 (return-from eval-for-inspect nil
)))))
119 (format stream
"~&~{~S~%~}" result-list
)))
121 (defun tty-display-inspected-parts (description named-p elements stream
)
122 (format stream
"~%~A" description
)
124 (dolist (element elements
)
126 (destructuring-bind (name . value
) element
127 (format stream
"~W. ~A: ~W~%" index name
128 (if (eq value
*inspect-unbound-object-marker
*)
131 (format stream
"~W. ~W~%" index element
))
136 ;;; Destructure an object for inspection, returning
137 ;;; (VALUES DESCRIPTION NAMED-P ELEMENTS),
140 ;;; DESCRIPTION is a summary description of the destructured object,
141 ;;; e.g. "The object is a CONS.~%".
143 ;;; NAMED-P determines what representation is used for elements
144 ;;; of ELEMENTS. If NAMED-P is true, then each element is
145 ;;; (CONS NAME VALUE); otherwise each element is just VALUE.
147 ;;; ELEMENTS is a list of the component parts of OBJECT (whose
148 ;;; representation is determined by NAMED-P).
150 ;;; (The NAMED-P dichotomy is useful because symbols and instances
151 ;;; need to display both a slot name and a value, while lists and
152 ;;; vectors need only display a value.)
153 (defgeneric inspected-parts
(object))
155 (defmethod inspected-parts ((object symbol
))
156 (values (format nil
"The object is a SYMBOL.~%")
158 (list (cons "Name" (symbol-name object
))
159 (cons "Package" (symbol-package object
))
160 (cons "Value" (if (boundp object
)
161 (symbol-value object
)
162 *inspect-unbound-object-marker
*))
163 (cons "Function" (if (fboundp object
)
164 (symbol-function object
)
165 *inspect-unbound-object-marker
*))
166 (cons "Plist" (symbol-plist object
)))))
168 (defun inspected-structure-elements (object)
169 (let ((parts-list '())
170 (info (layout-info (sb-kernel:layout-of object
))))
171 (when (sb-kernel::defstruct-description-p info
)
172 (dolist (dd-slot (dd-slots info
) (nreverse parts-list
))
173 (push (cons (dsd-name dd-slot
)
174 (funcall (dsd-accessor-name dd-slot
) object
))
177 (defmethod inspected-parts ((object structure-object
))
178 (values (format nil
"The object is a STRUCTURE-OBJECT of type ~S.~%"
181 (inspected-structure-elements object
)))
183 (defun inspected-standard-object-elements (object)
184 (let ((reversed-elements nil
)
185 (class-slots (sb-pcl::class-slots
(class-of object
))))
186 (dolist (class-slot class-slots
(nreverse reversed-elements
))
187 (let* ((slot-name (slot-value class-slot
'sb-pcl
::name
))
188 (slot-value (if (slot-boundp object slot-name
)
189 (slot-value object slot-name
)
190 *inspect-unbound-object-marker
*)))
191 (push (cons slot-name slot-value
) reversed-elements
)))))
193 (defmethod inspected-parts ((object standard-object
))
194 (values (format nil
"The object is a STANDARD-OBJECT of type ~S.~%"
197 (inspected-standard-object-elements object
)))
199 (defmethod inspected-parts ((object sb-mop
:funcallable-standard-object
))
200 (values (format nil
"The object is a ~S of type ~S.~%"
201 'sb-mop
:funcallable-standard-object
(type-of object
))
203 (inspected-standard-object-elements object
)))
205 (defmethod inspected-parts ((object condition
))
206 (values (format nil
"The object is a CONDITION of type ~S.~%"
209 (inspected-standard-object-elements object
)))
211 (defmethod inspected-parts ((object function
))
212 (values (format nil
"The object is a ~A named ~S.~%"
213 (if (closurep object
) 'closure
'function
)
214 (nth-value 2 (function-lambda-expression object
)))
216 ;; Defined-from stuff used to be here. Someone took
217 ;; it out. FIXME: We should make it easy to get
218 ;; to DESCRIBE from the inspector.
220 (cons "Lambda-list" (%fun-lambda-list object
))
221 (cons "Ftype" (%fun-type object
))
222 (when (closurep object
)
224 (cons "Closed over values" (%closure-values object
)))))))
227 (defmethod inspected-parts ((object sb-eval
:interpreted-function
))
228 (multiple-value-bind (defn closurep name
) (function-lambda-expression object
)
229 (declare (ignore closurep
))
230 (values (format nil
"The object is an interpreted function named ~S.~%" name
)
232 ;; Defined-from stuff used to be here. Someone took
233 ;; it out. FIXME: We should make it easy to get
234 ;; to DESCRIBE from the inspector.
236 (cons "Lambda-list" (sb-eval:interpreted-function-lambda-list object
))
237 (cons "Definition" defn
)
238 (cons "Documentation" (sb-eval:interpreted-function-documentation object
))))))
241 (defmethod inspected-parts ((object sb-interpreter
:interpreted-function
))
242 (multiple-value-bind (defn closurep name
) (function-lambda-expression object
)
243 (declare (ignore closurep
))
244 (values (format nil
"The object is an interpreted function named ~S.~%" name
)
246 ;; Defined-from stuff used to be here. Someone took
247 ;; it out. FIXME: We should make it easy to get
248 ;; to DESCRIBE from the inspector.
250 (cons "Lambda-list" (sb-interpreter:fun-lambda-list object
))
251 (cons "Definition" defn
)
252 (cons "Documentation" (sb-interpreter:fun-docstring object
))))))
254 (defmethod inspected-parts ((object vector
))
256 "The object is a ~:[~;displaced ~]VECTOR of length ~W.~%"
257 (and (array-header-p object
)
258 (%array-displaced-p object
))
261 ;; FIXME: Should we respect *INSPECT-LENGTH* here? If not, what
262 ;; does *INSPECT-LENGTH* mean?
263 (coerce object
'list
)))
265 (defun inspected-index-string (index rev-dimensions
)
266 (if (null rev-dimensions
)
269 (dolist (dim rev-dimensions
)
270 (multiple-value-bind (q r
) (floor index dim
)
273 (format nil
"[~W~{,~W~}]" (car list
) (cdr list
)))))
275 (defmethod inspected-parts ((object array
))
276 (let* ((length (min (array-total-size object
) *inspect-length
*))
277 (reference-array (make-array length
278 :element-type
(array-element-type object
)
279 :displaced-to object
))
280 (dimensions (array-dimensions object
))
281 (reversed-elements nil
))
282 ;; FIXME: Should we respect *INSPECT-LENGTH* here? If not, what does
283 ;; *INSPECT-LENGTH* mean?
285 (push (cons (format nil
287 (inspected-index-string i
(reverse dimensions
)))
288 (aref reference-array i
))
290 (values (format nil
"The object is ~:[a displaced~;an~] ARRAY of ~A.~%~
291 Its dimensions are ~S.~%"
292 (array-element-type object
)
293 (and (array-header-p object
)
294 (%array-displaced-p object
))
297 (nreverse reversed-elements
))))
299 (defmethod inspected-parts ((object cons
))
300 (if (consp (cdr object
))
301 (inspected-parts-of-nontrivial-list object
)
302 (inspected-parts-of-simple-cons object
)))
304 (defun inspected-parts-of-simple-cons (object)
305 (values "The object is a CONS.
308 (list (cons 'car
(car object
))
309 (cons 'cdr
(cdr object
)))))
311 (defun inspected-parts-of-nontrivial-list (object)
314 (reversed-elements nil
))
315 (flet ((done (description-format)
316 (return-from inspected-parts-of-nontrivial-list
317 (values (format nil description-format length
)
319 (nreverse reversed-elements
)))))
321 (cond ((null in-list
)
322 (done "The object is a proper list of length ~S.~%"))
323 ((>= length
*inspect-length
*)
324 (push (cons 'rest in-list
) reversed-elements
)
325 (done "The object is a long list (more than ~S elements).~%"))
327 (push (cons length
(pop in-list
)) reversed-elements
)
330 (push (cons 'rest in-list
) reversed-elements
)
331 (done "The object is an improper list of length ~S.~%")))))))
333 (defmethod inspected-parts ((object t
))
334 (values (format nil
"The object is an ATOM:~% ~W~%" object
) nil nil
))