1.0.41.1: much faster MAKE-INITIAL-DFUN
[sbcl.git] / contrib / sb-aclrepl / inspect.lisp
1 ;;;; Inspector for sb-aclrepl
2 ;;;;
3 ;;;; The documentation, which may or may not apply in its entirety at
4 ;;;; any given time, for this functionality is on the ACL website:
5 ;;;;   <http://www.franz.com/support/documentation/6.2/doc/inspector.htm>.
6 ;;;;
7 ;;;; A summary of inspector navigation is contained in the below *INSPECT-HELP*
8 ;;;; variable.
9
10 (cl:in-package #:sb-aclrepl)
11
12 (eval-when (:compile-toplevel :load-toplevel :execute)
13   (defconstant +default-inspect-length+ 20))
14
15 (defstruct (%inspect (:constructor make-inspect)
16                      (:conc-name inspect-))
17   ;; stack of parents of inspected object
18   object-stack
19   ;;  a stack of indices of parent object components
20   select-stack)
21
22 ;; FIXME - raw mode isn't currently used in object display
23 (defparameter *current-inspect* nil
24   "current inspect")
25 (defparameter *inspect-raw* nil
26   "Raw mode for object display.")
27 (defparameter *inspect-length* +default-inspect-length+
28   "maximum number of components to print")
29 (defparameter *skip-address-display* nil
30   "Skip displaying addresses of objects.")
31
32 (defvar *inspect-help*
33   ":istep takes between 0 to 3 arguments.
34 The commands are:
35 :i             redisplay current object
36 :i =           redisplay current object
37 :i nil         redisplay current object
38 :i ?           display this help
39 :i *           inspect the current * value
40 :i + <form>    inspect the (eval form)
41 :i slot <name> inspect component of object, even if name is an istep cmd
42 :i <index>     inspect the numbered component of object
43 :i <name>      inspect the named component of object
44 :i <form>      evaluation and inspect form
45 :i -           inspect parent
46 :i ^           inspect parent
47 :i <           inspect previous parent component
48 :i >           inspect next parent component
49 :i set <index> <form> set indexed component to evalated form
50 :i print <max> set the maximum number of components to print
51 :i skip <n>    skip a number of components when printing
52 :i tree        print inspect stack
53 ")
54
55 ;;; When *INSPECT-UNBOUND-OBJECT-MARKER* occurs in a parts list, it
56 ;;; indicates that that a slot is unbound.
57 (eval-when (:compile-toplevel :load-toplevel :execute)
58   (defvar *inspect-unbound-object-marker* (gensym "INSPECT-UNBOUND-OBJECT-")))
59
60
61 (defun inspector-fun (object input-stream output-stream)
62   (let ((*current-inspect* nil)
63         (*inspect-raw* nil)
64         (*inspect-length* *inspect-length*)
65         (*skip-address-display* nil))
66     (setq *current-inspect* (make-inspect))
67     (reset-stack object "(inspect ...)")
68     (redisplay output-stream)
69     (let ((*input* input-stream)
70           (*output* output-stream))
71       (repl :inspect t)))
72   (values))
73
74 (setq sb-impl::*inspect-fun* #'inspector-fun)
75
76 (defun istep (args stream)
77   (unless *current-inspect*
78     (setq *current-inspect* (make-inspect)))
79   (istep-dispatch args
80                   (first args)
81                   (when (first args) (read-from-string (first args)))
82                   stream))
83
84 (defun istep-dispatch (args option-string option stream)
85   (cond
86     ((or (string= "=" option-string) (zerop (length args)))
87      (istep-cmd-redisplay stream))
88     ((or (string= "-" option-string) (string= "^" option-string))
89      (istep-cmd-parent stream))
90     ((string= "*" option-string)
91      (istep-cmd-inspect-* stream))
92     ((string= "+" option-string)
93      (istep-cmd-inspect-new-form (read-from-string (second args)) stream))
94     ((or (string= "<" option-string)
95          (string= ">" option-string))
96      (istep-cmd-select-parent-component option-string stream))
97     ((string-equal "set" option-string)
98      (istep-cmd-set (second args) (third args) stream))
99     ((string-equal "raw" option-string)
100      (istep-cmd-set-raw (second args) stream))
101     ((string-equal "q" option-string)
102      (istep-cmd-reset))
103     ((string-equal "?" option-string)
104      (istep-cmd-help stream))
105     ((string-equal "skip" option-string)
106      (istep-cmd-skip (second args) stream))
107     ((string-equal "tree" option-string)
108      (istep-cmd-tree stream))
109     ((string-equal "print" option-string)
110      (istep-cmd-print (second args) stream))
111     ((string-equal "slot" option-string)
112      (istep-cmd-select-component (read-from-string (second args)) stream))
113     ((or (symbolp option)
114          (integerp option))
115      (istep-cmd-select-component option stream))
116     (t
117      (istep-cmd-set-stack option stream))))
118
119 (defun set-current-inspect (inspect)
120   (setq *current-inspect* inspect))
121
122 (defun reset-stack (&optional object label)
123   (cond
124     ((null label)
125      (setf (inspect-object-stack *current-inspect*) nil)
126      (setf (inspect-select-stack *current-inspect*) nil))
127     (t
128      (setf (inspect-object-stack *current-inspect*) (list object))
129      (setf (inspect-select-stack *current-inspect*) (list label)))))
130
131 (defun output-inspect-note (stream note &rest args)
132   (apply #'format stream note args)
133   (princ #\Newline stream))
134
135 (defun stack ()
136   (inspect-object-stack *current-inspect*))
137
138 (defun redisplay (stream &optional (skip 0))
139   (display-current stream *inspect-length* skip))
140
141 ;;;
142 ;;; istep command processing
143 ;;;
144
145 (defun istep-cmd-redisplay (stream)
146   (redisplay stream))
147
148 (defun istep-cmd-parent (stream)
149   (cond
150     ((> (length (inspect-object-stack *current-inspect*)) 1)
151      (setf (inspect-object-stack *current-inspect*)
152            (cdr (inspect-object-stack *current-inspect*)))
153      (setf (inspect-select-stack *current-inspect*)
154            (cdr (inspect-select-stack *current-inspect*)))
155      (redisplay stream))
156     ((stack)
157        (output-inspect-note stream "Object has no parent"))
158     (t
159      (no-object-msg stream))))
160
161 (defun istep-cmd-inspect-* (stream)
162   (reset-stack * "(inspect *)")
163   (redisplay stream))
164
165 (defun istep-cmd-inspect-new-form (form stream)
166   (inspector-fun (eval form) nil stream))
167
168 (defun istep-cmd-select-parent-component (option stream)
169   (if (stack)
170       (if (eql (length (stack)) 1)
171           (output-inspect-note stream "Object does not have a parent")
172           (let ((parent (second (stack)))
173                 (id (car (inspect-select-stack *current-inspect*))))
174             (multiple-value-bind (position parts)
175                 (find-part-id parent id)
176               (let ((new-position (if (string= ">" option)
177                                       (1+ position)
178                                       (1- position))))
179                 (if (< -1 new-position (parts-count parts))
180                     (let* ((value (component-at parts new-position)))
181                       (setf (car (inspect-object-stack *current-inspect*))
182                             value)
183                       (setf (car (inspect-select-stack *current-inspect*))
184                             (id-at parts new-position))
185                       (redisplay stream))
186                     (output-inspect-note stream
187                                          "Parent has no selectable component indexed by ~d"
188                                          new-position))))))
189       (no-object-msg stream)))
190
191 (defun istep-cmd-set-raw (option-string stream)
192   (when (inspect-object-stack *current-inspect*)
193     (cond
194       ((null option-string)
195        (setq *inspect-raw* t))
196       ((eq (read-from-string option-string) t)
197        (setq *inspect-raw* t))
198       ((eq (read-from-string option-string) nil)
199        (setq *inspect-raw* nil)))
200     (redisplay stream)))
201
202 (defun istep-cmd-reset ()
203   (reset-stack)
204   (throw 'repl-catcher (values :inspect nil)))
205
206 (defun istep-cmd-help (stream)
207   (format stream *inspect-help*))
208
209 (defun istep-cmd-skip (option-string stream)
210   (if option-string
211       (let ((len (read-from-string option-string)))
212         (if (and (integerp len) (>= len 0))
213             (redisplay stream len)
214             (output-inspect-note stream "Skip length invalid")))
215       (output-inspect-note stream "Skip length missing")))
216
217 (defun istep-cmd-print (option-string stream)
218   (if option-string
219       (let ((len (read-from-string option-string)))
220         (if (and (integerp len) (plusp len))
221             (setq *inspect-length* len)
222             (output-inspect-note stream "Cannot set print limit to ~A~%" len)))
223       (output-inspect-note stream "Print length missing")))
224
225 (defun select-description (select)
226   (typecase select
227     (integer
228      (format nil "which is componenent number ~d of" select))
229     (symbol
230      (format nil "which is the ~a component of" select))
231     (string
232      (format nil "which was selected by ~A" select))
233     (t
234      (write-to-string select))))
235
236 (defun istep-cmd-tree (stream)
237   (let ((stack (inspect-object-stack *current-inspect*)))
238     (if stack
239         (progn
240           (output-inspect-note stream "The current object is:")
241           (dotimes (i (length stack))
242             (output-inspect-note
243                stream "~A, ~A"
244                (inspected-description (nth i stack))
245                (select-description
246                 (nth i (inspect-select-stack *current-inspect*))))))
247         (no-object-msg stream))))
248
249 (defun istep-cmd-set (id-string value-string stream)
250   (if (stack)
251       (let ((id (when id-string (read-from-string id-string))))
252         (multiple-value-bind (position parts)
253             (find-part-id (car (stack)) id)
254           (if parts
255               (if position
256                   (when value-string
257                     (let ((new-value (eval (read-from-string value-string))))
258                       (let ((result (set-component-value (car (stack))
259                                                          id
260                                                          new-value
261                                                          (component-at
262                                                           parts position))))
263                         (typecase result
264                           (string
265                            (output-inspect-note stream result))
266                           (t
267                            (redisplay stream))))))
268                   (output-inspect-note
269                    stream
270                    "Object has no selectable component named by ~A" id))
271               (output-inspect-note stream
272                                    "Object has no selectable components"))))
273       (no-object-msg stream)))
274
275 (defun istep-cmd-select-component (id stream)
276   (if (stack)
277       (multiple-value-bind (position parts)
278           (find-part-id (car (stack)) id)
279         (cond
280           ((integerp position)
281            (let* ((value (component-at parts position)))
282              (cond ((eq value *inspect-unbound-object-marker*)
283                     (output-inspect-note stream "That slot is unbound"))
284                    (t
285                     (push value (inspect-object-stack *current-inspect*))
286                     (push id (inspect-select-stack *current-inspect*))
287                     (redisplay stream)))))
288           ((null parts)
289            (output-inspect-note stream "Object does not contain any subobjects"))
290           (t
291            (typecase id
292              (symbol
293               (output-inspect-note
294                stream "Object has no selectable component named ~A"
295                id))
296              (integer
297               (output-inspect-note
298                stream "Object has no selectable component indexed by ~d"
299                id))))))
300       (no-object-msg stream)))
301
302 (defun istep-cmd-set-stack (form stream)
303   (reset-stack (eval form) ":i ...")
304   (redisplay stream))
305
306
307 (defun no-object-msg (s)
308   (output-inspect-note s "No object is being inspected"))
309
310 (defun display-current (s length skip)
311   (if (stack)
312       (let ((inspected (car (stack))))
313         (setq cl:* inspected)
314         (display-inspect inspected s length skip))
315       (no-object-msg s)))
316
317
318 ;;;
319 ;;; aclrepl-specific inspection display
320 ;;;
321
322 (defun display-inspect (object stream &optional length (skip 0))
323   (multiple-value-bind (elements labels count)
324       (inspected-elements object length skip)
325     (fresh-line stream)
326     (format stream "~A" (inspected-description object))
327     (unless (or *skip-address-display*
328                 (eq object *inspect-unbound-object-marker*)
329                 (and (= sb-vm::n-word-bits 64) (typep object 'single-float))
330                 (characterp object) (typep object 'fixnum))
331       (write-string " at #x" stream)
332       (format stream (n-word-bits-hex-format)
333               (logand (sb-kernel:get-lisp-obj-address object)
334                       (lognot sb-vm:lowtag-mask))))
335     (dotimes (i count)
336       (fresh-line stream)
337       (display-labeled-element (elt elements i) (elt labels i) stream))))
338
339 (defun array-label-p (label)
340   (and (consp label)
341        (stringp (cdr label))
342        (char= (char (cdr label) 0) #\[)))
343
344 (defun named-or-array-label-p (label)
345   (and (consp label) (not (hex-label-p label))))
346
347 (defun hex-label-p (label &optional width)
348   (and (consp label)
349        (case width
350              (32 (eq (cdr label) :hex32))
351              (64 (eq (cdr label) :hex64))
352              (t (or (eq (cdr label) :hex32)
353                     (eq (cdr label) :hex64))))))
354
355 (defun display-labeled-element (element label stream)
356   (cond
357     ((eq label :ellipses)
358      (format stream "   ..."))
359     ((eq label :tail)
360      (format stream "tail-> ~A" (inspected-description element)))
361     ((named-or-array-label-p label)
362      (format stream
363              (if (array-label-p label)
364                  "~4,' D ~A-> ~A"
365                  "~4,' D ~16,1,1,'-A> ~A")
366              (car label)
367              (format nil "~A " (cdr label))
368              (inspected-description element)))
369     ((hex-label-p label 32)
370      (format stream "~4,' D-> #x~8,'0X" (car label) element))
371     ((hex-label-p label 64)
372      (format stream "~4,' D-> #x~16,'0X" (car label) element))
373     (t
374      (format stream "~4,' D-> ~A" label (inspected-description element)))))
375
376 ;;; THE BEGINNINGS OF AN INSPECTOR API
377 ;;; which can be used to retrieve object descriptions as component values/labels and also
378 ;;; process print length and skip selectors
379 ;;;
380 ;;; FUNCTIONS TO CONSIDER FOR EXPORT
381 ;;;   FIND-PART-ID
382 ;;;   COMPONENT-AT
383 ;;;   ID-AT
384 ;;;   INSPECTED-ELEMENTS
385 ;;;   INSPECTED-DESCRIPTION
386 ;;;
387 ;;; will also need hooks
388 ;;;    *inspect-start-inspection*
389 ;;;       (maybe. Would setup a window for a GUI inspector)
390 ;;;    *inspect-prompt-fun*
391 ;;;    *inspect-read-cmd*
392 ;;;
393 ;;; and, either an *inspect-process-cmd*, or *inspect-display* hook
394 ;;; That'll depend if choose to have standardized inspector commands such that
395 ;;; (funcall *inspect-read-cmd*) will return a standard command that SBCL will
396 ;;; process and then call the *inspect-display* hook, or if the
397 ;;; *inspect-read-cmd* will return an impl-dependent cmd that sbcl will
398 ;;; send to the contributed inspector for processing and display.
399
400 (defun find-part-id (object id)
401   "COMPONENT-ID can be an integer or a name of a id.
402 Returns (VALUES POSITION PARTS).
403 POSITION is NIL if the id is invalid or not found."
404   (let* ((parts (inspected-parts object))
405          (name (if (symbolp id) (symbol-name id) id)))
406     (values
407      (cond
408        ((and (numberp id)
409              (< -1 id (parts-count parts))
410              (not (eq (parts-seq-type parts) :bignum)))
411         id)
412        (t
413         (case (parts-seq-type parts)
414           (:named
415            (position name (the list (parts-components parts))
416                      :key #'car :test #'string-equal))
417           ((:dotted-list :cyclic-list)
418            (when (string-equal name "tail")
419              (1- (parts-count parts)))))))
420      parts)))
421
422 (defun component-at (parts position)
423   (let ((count (parts-count parts))
424         (components (parts-components parts)))
425     (when (< -1 position count)
426       (case (parts-seq-type parts)
427         (:dotted-list
428          (if (= position (1- count))
429              (cdr (last components))
430              (elt components position)))
431         (:cyclic-list
432          (if (= position (1- count))
433              components
434              (elt components position)))
435         (:named
436          (cdr (elt components position)))
437         (:array
438          (aref (the array components) position))
439         (:bignum
440          (bignum-component-at components position))
441         (t
442          (elt components position))))))
443
444 (defun id-at (parts position)
445   (let ((count (parts-count parts)))
446     (when (< -1 position count)
447       (case (parts-seq-type parts)
448         ((:dotted-list :cyclic-list)
449          (if (= position (1- count))
450              :tail
451              position))
452         (:array
453          (array-index-string position parts))
454         (:named
455          (car (elt (parts-components parts) position)))
456         (t
457          position)))))
458
459 (defun inspected-elements (object &optional length (skip 0))
460   "Returns elements of an object that have been trimmed and labeled based on
461 length and skip. Returns (VALUES ELEMENTS LABELS ELEMENT-COUNT)
462 where ELEMENTS and LABELS are vectors containing ELEMENT-COUNT items.
463 LABELS elements may be a string, number, cons pair, :tail, or :ellipses.
464 This function may return an ELEMENT-COUNT of up to (+ 3 length) which would
465 include an :ellipses at the beginning, :ellipses at the end,
466 and the last element."
467   (let* ((parts (inspected-parts object))
468          (print-length (if length length (parts-count parts)))
469          (last-part (last-part parts))
470          (last-requested (last-requested parts print-length skip))
471          (element-count (compute-elements-count parts print-length skip))
472          (first-to (if (first-element-ellipses-p parts skip) 1 0))
473          (elements (when (plusp element-count) (make-array element-count)))
474          (labels (when (plusp element-count) (make-array element-count))))
475     (when (plusp element-count)
476       ;; possible first ellipses
477       (when (first-element-ellipses-p parts skip)
478         (set-element-values elements labels 0 nil :ellipses))
479       ;; main elements
480       (do* ((i 0 (1+ i)))
481            ((> i (- last-requested skip)))
482         (set-element elements labels parts (+ i first-to) (+ i skip)))
483       ;; last parts value if needed
484       (when (< last-requested last-part)
485         (set-element elements labels parts (- element-count 1) last-part))
486       ;; ending ellipses or next to last parts value if needed
487       (when (< last-requested (1- last-part))
488         (if (= last-requested (- last-part 2))
489             (set-element elements labels parts (- element-count 2) (1- last-part))
490             (set-element-values elements labels (- element-count 2) nil :ellipses))))
491     (values elements labels element-count)))
492
493 (defun last-requested (parts print skip)
494   (min (1- (parts-count parts)) (+ skip print -1)))
495
496 (defun last-part (parts)
497   (1- (parts-count parts)))
498
499 (defun compute-elements-count (parts length skip)
500   "Compute the number of elements in parts given the print length and skip."
501   (let ((element-count (min (parts-count parts) length
502                             (max 0 (- (parts-count parts) skip)))))
503     (when (and (plusp (parts-count parts)) (plusp skip)) ; starting ellipses
504       (incf element-count))
505     (when (< (last-requested parts length skip)
506              (last-part parts)) ; last value
507       (incf element-count)
508       (when (< (last-requested parts length skip)
509                (1- (last-part parts))) ; ending ellipses
510         (incf element-count)))
511     element-count))
512
513 (defun set-element (elements labels parts to-index from-index)
514   (set-element-values elements labels to-index (component-at parts from-index)
515                       (label-at parts from-index)))
516
517 (defun set-element-values (elements labels index element label)
518   (setf (aref elements index) element)
519   (setf (aref labels index) label))
520
521 (defun first-element-ellipses-p (parts skip)
522   (and (parts-count parts) (plusp skip)))
523
524 (defun label-at (parts position)
525   "Helper function for inspected-elements. Conses the
526 position with the label if the label is a string."
527   (let ((id (id-at parts position)))
528     (cond
529       ((stringp id)
530        (cons position id))
531       ((eq (parts-seq-type parts) :bignum)
532        (cons position (case sb-vm::n-word-bits
533                             (32 :hex32)
534                             (64 :hex64))))
535       (t
536         id))))
537
538 (defun array-index-string (index parts)
539   "Formats an array index in row major format."
540   (let ((rev-dimensions (parts-seq-hint parts)))
541     (if (null rev-dimensions)
542         "[]"
543         (let ((list nil))
544           (dolist (dim rev-dimensions)
545             (multiple-value-bind (q r) (floor index dim)
546               (setq index q)
547               (push r list)))
548           (format nil "[~W~{,~W~}]" (car list) (cdr list))))))
549
550 \f
551 ;;; INSPECTED-DESCRIPTION
552 ;;;
553 ;;; Accepts an object and returns
554 ;;;   DESCRIPTION is a summary description of the destructured object,
555 ;;;   e.g. "the object is a CONS".
556
557 (defgeneric inspected-description (object))
558
559 (defmethod inspected-description ((object symbol))
560   (format nil "the symbol ~A" object))
561
562 (defmethod inspected-description ((object structure-object))
563   (format nil "~W" (find-class (type-of object))))
564
565 (defmethod inspected-description ((object package))
566   (format nil "the ~A package" (package-name object)))
567
568 (defmethod inspected-description ((object standard-object))
569   (format nil "~W" (class-of object)))
570
571 (defmethod inspected-description ((object function))
572   (format nil "~S" object) nil)
573
574 (defmethod inspected-description ((object vector))
575   (declare (vector object))
576   (format nil "a ~:[~;displaced ~]vector (~W)"
577           (and (sb-kernel:array-header-p object)
578                (sb-kernel:%array-displaced-p object))
579           (length object)))
580
581 (defmethod inspected-description ((object simple-vector))
582   (declare (simple-vector object))
583   (format nil "a simple ~A vector (~D)"
584           (array-element-type object)
585           (length object)))
586
587 (defmethod inspected-description ((object array))
588   (declare (array object))
589   (format nil "~:[A displaced~;An~] array of ~A with dimensions ~W"
590           (and (sb-kernel:array-header-p object)
591                (sb-kernel:%array-displaced-p object))
592           (array-element-type object)
593           (array-dimensions object)))
594
595 (defun simple-cons-pair-p (object)
596   (atom (cdr object)))
597
598 (defmethod inspected-description ((object cons))
599   (if (simple-cons-pair-p object)
600       "a cons cell"
601       (inspected-description-of-nontrivial-list object)))
602
603 (defun cons-safe-length (object)
604   "Returns (VALUES LENGTH LIST-TYPE) where length is the number of
605 cons cells and LIST-TYPE is :normal, :dotted, or :cyclic"
606     (do ((length 1 (1+ length))
607          (lst (cdr object) (cdr lst)))
608         ((or (not (consp lst))
609              (eq object lst))
610          (cond
611            ((null lst)
612             (values length :normal))
613            ((atom lst)
614             (values length :dotted))
615            ((eq object lst)
616             (values length :cyclic))))
617       ;; nothing to do in body
618       ))
619
620 (defun inspected-description-of-nontrivial-list (object)
621   (multiple-value-bind (length list-type) (cons-safe-length object)
622     (format nil "a ~A list with ~D element~:*~P~A"
623             (string-downcase (symbol-name list-type)) length
624             (ecase list-type
625               ((:dotted :cyclic) "+tail")
626               (:normal "")))))
627
628 (defun n-word-bits-hex-format ()
629   (case sb-vm::n-word-bits
630     (64 "~16,'0X")
631     (32 "~8,'0X")
632     (t  "~X")))
633
634 (defun ref32-hexstr (obj &optional (offset 0))
635   (format nil "~8,'0X" (ref32 obj offset)))
636
637 (defun ref32 (obj &optional (offset 0))
638   (sb-sys::without-gcing
639    (sb-sys:sap-ref-32
640     (sb-sys:int-sap
641      (logand (sb-kernel:get-lisp-obj-address obj) (lognot sb-vm:lowtag-mask)))
642     offset)))
643
644 (defun description-maybe-internals (fmt objects internal-fmt &rest args)
645   (let ((base (apply #'format nil fmt objects)))
646     (if *skip-address-display*
647         base
648         (concatenate 'string
649                      base " " (apply #'format nil internal-fmt args)))))
650
651 (defmethod inspected-description ((object double-float))
652   (let ((start (round (* 2 sb-vm::n-word-bits) 8)))
653     (description-maybe-internals "double-float ~W" (list object)
654                                  "[#~A ~A]"
655                                  (ref32-hexstr object (+ start 4))
656                                  (ref32-hexstr object start))))
657
658 (defmethod inspected-description ((object single-float))
659   (ecase sb-vm::n-word-bits
660     (32
661      (description-maybe-internals "single-float ~W" (list object)
662                                   "[#x~A]"
663                                   (ref32-hexstr object (round sb-vm::n-word-bits 8))))
664     (64
665      ;; on 64-bit platform, single-floats are not boxed
666      (description-maybe-internals "single-float ~W" (list object)
667                                   "[#x~8,'0X]"
668                                   (ash (sb-kernel:get-lisp-obj-address object) -32)))))
669
670 (defmethod inspected-description ((object fixnum))
671   (description-maybe-internals
672    "fixnum ~W" (list object)
673    (concatenate 'string "[#x" (n-word-bits-hex-format) "]")
674    (ash object (1- sb-vm:n-lowtag-bits))))
675
676 (defmethod inspected-description ((object complex))
677   (format nil "complex number ~W" object))
678
679 (defmethod inspected-description ((object simple-string))
680   (format nil "a simple-string (~W) ~W" (length object) object))
681
682 (defun bignum-words (bignum)
683   "Return the number of words in a bignum"
684   (ash
685    (logand (ref32 bignum) (lognot sb-vm:widetag-mask))
686    (- sb-vm:n-widetag-bits)))
687
688 (defun bignum-component-at (bignum offset)
689   "Return the word at offset"
690   (case sb-vm::n-word-bits
691         (32
692          (ref32 bignum (* 4 (1+ offset))))
693         (64
694          (let ((start (* 8 (1+ offset))))
695            (+ (ref32 bignum start)
696               (ash (ref32 bignum (+ 4 start)) 32))))))
697
698 (defmethod inspected-description ((object bignum))
699   (format nil  "bignum ~W with ~D ~A-bit word~P" object
700           (bignum-words object) sb-vm::n-word-bits (bignum-words object)))
701
702 (defmethod inspected-description ((object ratio))
703   (format nil "ratio ~W" object))
704
705 (defmethod inspected-description ((object character))
706   ;; FIXME: This will need to change as and when we get more characters
707   ;; than just the 256 we have today.
708   (description-maybe-internals
709    "character ~W char-code #x~2,'0X"
710    (list object (char-code object))
711    "[#x~8,'0X]"
712    (logior sb-vm:character-widetag (ash (char-code object)
713                                         sb-vm:n-widetag-bits))))
714
715 (defmethod inspected-description ((object t))
716   (format nil "a generic object ~W" object))
717
718 (defmethod inspected-description ((object (eql *inspect-unbound-object-marker*)))
719   "..unbound..")
720
721 \f
722 ;;; INSPECTED-PARTS
723 ;;;
724 ;;; Accepts the arguments OBJECT LENGTH SKIP and returns,
725 ;;;   (LIST COMPONENTS SEQ-TYPE COUNT SEQ-HINT)
726 ;;; where..
727 ;;;
728 ;;;   COMPONENTS are the component parts of OBJECT (whose
729 ;;;   representation is determined by SEQ-TYPE). Except for the
730 ;;;   SEQ-TYPE :named and :array, components is just the OBJECT itself
731 ;;;
732 ;;;   SEQ-TYPE determines what representation is used for components
733 ;;;   of COMPONENTS.
734 ;;;      If SEQ-TYPE is :named, then each element is (CONS NAME VALUE)
735 ;;;      If SEQ-TYPE is :dotted-list, then each element is just value,
736 ;;;        but the last element must be retrieved by
737 ;;;        (cdr (last components))
738 ;;;      If SEQ-TYPE is :cylic-list, then each element is just value,
739 ;;;      If SEQ-TYPE is :list, then each element is a value of an array
740 ;;;      If SEQ-TYPE is :vector, then each element is a value of an vector
741 ;;;      If SEQ-TYPE is :array, then each element is a value of an array
742 ;;;        with rank >= 2. The
743 ;;;      If SEQ-TYPE is :bignum, then object is just a bignum and not a
744 ;;;        a sequence
745 ;;;
746 ;;;   COUNT is the total number of components in the OBJECT
747 ;;;
748 ;;; SEQ-HINT is a seq-type dependent hint. Used by SEQ-TYPE :array
749 ;;; to hold the reverse-dimensions of the orignal array.
750
751 (declaim (inline parts-components))
752 (defun parts-components (parts)
753   (first parts))
754
755 (declaim (inline parts-count))
756 (defun parts-count (parts)
757   (second parts))
758
759 (declaim (inline parts-seq-type))
760 (defun parts-seq-type (parts)
761   (third parts))
762
763 (declaim (inline parts-seq-hint))
764 (defun parts-seq-hint (parts)
765   (fourth parts))
766
767 ;;; FIXME: Most of this should be refactored to share the code
768 ;;; with the vanilla inspector. Also, we should check what the
769 ;;; Slime inspector does, and provide a an interface for it to
770 ;;; use that would propagate any SBCL inspector improvements
771 ;;; automagically to Slime. -- ns 2005-02-20
772 (defgeneric inspected-parts (object))
773
774 (defmethod inspected-parts ((object symbol))
775   (let ((components
776          (list (cons "NAME" (symbol-name object))
777                (cons "PACKAGE" (symbol-package object))
778                (cons "VALUE" (if (boundp object)
779                                  (symbol-value object)
780                                  *inspect-unbound-object-marker*))
781                (cons "FUNCTION" (if (fboundp object)
782                                     (symbol-function object)
783                                     *inspect-unbound-object-marker*))
784                (cons "PLIST" (symbol-plist object)))))
785     (list components (length components) :named nil)))
786
787 (defun inspected-structure-parts (object)
788   (let ((components-list '())
789         (info (sb-kernel:layout-info (sb-kernel:layout-of object))))
790     (when (sb-kernel::defstruct-description-p info)
791       (dolist (dd-slot (sb-kernel:dd-slots info) (nreverse components-list))
792         (push (cons (string (sb-kernel:dsd-name dd-slot))
793                     (funcall (sb-kernel:dsd-accessor-name dd-slot) object))
794               components-list)))))
795
796 (defmethod inspected-parts ((object structure-object))
797   (let ((components (inspected-structure-parts object)))
798     (list components (length components) :named nil)))
799
800 (defun inspected-standard-object-parts (object)
801   (let ((components nil)
802         (class-slots (sb-pcl::class-slots (class-of object))))
803     (dolist (class-slot class-slots (nreverse components))
804       (let* ((slot-name (slot-value class-slot 'sb-pcl::name))
805              (slot-value (if (slot-boundp object slot-name)
806                              (slot-value object slot-name)
807                              *inspect-unbound-object-marker*)))
808         (push (cons (symbol-name slot-name) slot-value) components)))))
809
810
811 (defmethod inspected-parts ((object standard-object))
812   (let ((components (inspected-standard-object-parts object)))
813     (list components (length components) :named nil)))
814
815 (defmethod inspected-parts ((object condition))
816   (let ((components (inspected-standard-object-parts object)))
817     (list components (length components) :named nil)))
818
819 (defmethod inspected-parts ((object function))
820   (let ((components (list (cons "arglist" (sb-kernel:%fun-lambda-list object)))))
821     (list components (length components) :named nil)))
822
823 (defmethod inspected-parts ((object vector))
824   (list object (length object) :vector nil))
825
826 (defmethod inspected-parts ((object array))
827   (let ((size (array-total-size object)))
828     (list (make-array size
829                       :element-type (array-element-type object)
830                       :displaced-to object)
831             size
832             :array
833             (reverse (array-dimensions object)))))
834
835 (defmethod inspected-parts ((object cons))
836   (if (simple-cons-pair-p object)
837       (inspected-parts-of-simple-cons object)
838       (inspected-parts-of-nontrivial-list object)))
839
840 (defun inspected-parts-of-simple-cons (object)
841   (let ((components (list (cons "car" (car object))
842                         (cons "cdr" (cdr object)))))
843     (list components 2 :named nil)))
844
845 (defun inspected-parts-of-nontrivial-list (object)
846     (multiple-value-bind (count list-type) (cons-safe-length object)
847       (case list-type
848         (:normal
849          (list object count :list nil))
850         (:cyclic
851          (list object (1+ count) :cyclic-list nil))
852         (:dotted
853          ;; count tail element
854          (list object (1+ count) :dotted-list nil)))))
855
856 (defmethod inspected-parts ((object complex))
857   (let ((components (list (cons "real" (realpart object))
858                         (cons "imag" (imagpart object)))))
859     (list components (length components) :named nil)))
860
861 (defmethod inspected-parts ((object ratio))
862   (let ((components (list (cons "numerator" (numerator object))
863                         (cons "denominator" (denominator object)))))
864     (list components (length components) :named nil)))
865
866 (defmethod inspected-parts ((object bignum))
867     (list object (bignum-words object) :bignum nil))
868
869 (defmethod inspected-parts ((object t))
870   (list nil 0 nil nil))
871
872
873 ;; FIXME - implement setting of component values
874
875 (defgeneric set-component-value (object component-id value element))
876
877 (defmethod set-component-value ((object cons) id value element)
878   (format nil "Cons object does not support setting of component ~A" id))
879
880 (defmethod set-component-value ((object array) id value element)
881   (format nil "Array object does not support setting of component ~A" id))
882
883 (defmethod set-component-value ((object symbol) id value element)
884   (format nil "Symbol object does not support setting of component ~A" id))
885
886 (defmethod set-component-value ((object structure-object) id value element)
887   (format nil "Structure object does not support setting of component ~A" id))
888
889 (defmethod set-component-value ((object standard-object) id value element)
890   (format nil "Standard object does not support setting of component ~A" id))
891
892 (defmethod set-component-value ((object t) id value element)
893   (format nil "Object does not support setting of component ~A" id))