1 ;;;; the 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")
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.
28 ;;; *INSPECT-OBJECT-STACK* is an assoc list of objects to their parts.
29 (defvar *inspect-object-stack* ())
31 (defparameter *inspect-length* 10)
33 #-sb-fluid (declaim (inline numbered-parts-p))
34 (defun numbered-parts-p (parts)
37 (defconstant parts-offset 2)
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)))
44 (defun inspect (object)
46 (input-loop object (describe-parts object) *standard-output*)
47 (setf *inspect-object-stack* nil)))
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))
53 (defun input-loop (object parts s)
54 (tty-display-object parts s)
58 (let ((command (read))
59 ;; Use 2 less than the length because the first 2 elements
61 (parts-len-2 (- (length parts) 2)))
64 (cond ((< -1 command parts-len-2)
65 (cond ((eq (nth-parts parts command) *illegal-object-marker*)
66 (format s "~%That slot is unbound.~%"))
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))))
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))))))
78 (case (find-symbol (symbol-name command) *keyword-package*)
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.~%"))))
89 (setf parts (describe-parts object))
90 (tty-display-object parts s))
92 (tty-display-object parts s))
96 (do-inspect-eval command s))))
98 (do-inspect-eval command s))))))
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~%~}" /)))
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))
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))
127 (format stream "~D. ~A: ~A~%" i (caar part)
128 (if (eq (cdar part) *illegal-object-marker*)
131 (format stream "~D. ~A~%" i (car part))))))
135 (defun describe-parts (object)
137 (symbol (describe-symbol-parts object))
138 (instance (describe-instance-parts object :structure))
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))))
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))))
159 (defun describe-instance-parts (object kind)
160 (let ((info (layout-info (sb-kernel:layout-of object)))
162 (push (format nil "~S is a ~(~A~).~%" object kind) 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))
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)
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.
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))
189 (coerce object 'list)))
191 (defun describe-cons-parts (object)
192 (list* (format nil "The object is a LIST of length ~D.~%" (length object))
196 (defun index-string (index rev-dimensions)
197 (if (null rev-dimensions)
200 (dolist (dim rev-dimensions)
201 (multiple-value-bind (q r) (floor index dim)
204 (format nil "[~D~{,~D~}]" (car list) (cdr list)))))
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))
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))
219 (dotimes (i length (nreverse parts))
220 (push (cons (format nil "~A " (index-string i (reverse dimensions)))
221 (aref reference-array i))
224 (defun describe-atomic-parts (object)
225 (list (format nil "The object is an atom.~%") nil object))