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