1 ;;;; the DESCRIBE system
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 ;;; SB-IMPL, not SB!IMPL, since we're built in warm load.
13 (in-package "SB-IMPL")
15 ;;;; Utils, move elsewhere.
17 (defun class-name-or-class (class)
18 (let ((name (class-name class)))
19 (if (eq class (find-class name nil))
24 (if (typep x 'generic-function)
25 (sb-pcl:generic-function-name x)
28 ;;; Prints X on a single line, limiting output length by *PRINT-RIGHT-MARGIN*
29 ;;; -- good for printing object parts, etc.
30 (defun prin1-to-line (x &key (columns 1) (reserve 0))
31 (let* ((line (write-to-string x :escape t :readably nil :lines 2 :circle t))
32 (p (position #\newline line))
33 (limit (truncate (- *print-right-margin* reserve) columns)))
34 (flet ((trunc (&optional end)
35 (let ((line-end (- limit 2)))
36 (with-output-to-string (s)
37 (write-string line s :end (if end
40 (write-string ".." s)))))
43 ((> (length line) limit)
48 (defun describe (object &optional (stream-designator *standard-output*))
50 "Print a description of OBJECT to STREAM-DESIGNATOR."
51 (let ((stream (out-synonym-of stream-designator))
52 (*print-right-margin* (or *print-right-margin* 72))
54 (*suppress-print-errors*
55 (if (subtypep 'serious-condition *suppress-print-errors*)
56 *suppress-print-errors*
58 ;; Until sbcl-0.8.0.x, we did
59 ;; (FRESH-LINE STREAM)
60 ;; (PPRINT-LOGICAL-BLOCK (STREAM NIL)
62 ;; here. However, ANSI's specification of DEFUN DESCRIBE,
63 ;; DESCRIBE exists as an interface primarily to manage argument
64 ;; defaulting (including conversion of arguments T and NIL into
65 ;; stream objects) and to inhibit any return values from
67 ;; doesn't mention either FRESH-LINEing or PPRINT-LOGICAL-BLOCKing,
68 ;; and the example of typical DESCRIBE-OBJECT behavior in ANSI's
69 ;; specification of DESCRIBE-OBJECT will work poorly if we do them
70 ;; here. (The example method for DESCRIBE-OBJECT does its own
71 ;; FRESH-LINEing, which is a physical directive which works poorly
72 ;; inside a pretty-printer logical block.)
73 (handler-bind ((print-not-readable #'print-unreadably))
74 (describe-object object stream))
75 ;; We don't TERPRI here either (any more since sbcl-0.8.0.x), because
76 ;; again ANSI's specification of DESCRIBE doesn't mention it and
77 ;; ANSI's example of DESCRIBE-OBJECT does its own final TERPRI.
84 ;;;; * Each interesting class has a primary method of its own.
86 ;;;; * Output looks like
88 ;;;; object-self-string
89 ;;;; [object-type-string]
98 ;;;; * The newline policy that gets the whitespace right is for
99 ;;;; each block to both start and end with a newline.
101 (defgeneric object-self-string (x))
103 (defmethod object-self-string (x)
106 (defmethod object-self-string ((x symbol))
107 (let ((*package* (find-package :keyword)))
108 (prin1-to-string x)))
110 (defgeneric object-type-string (x))
112 (defmethod object-type-string (x)
113 (let ((type (class-name-or-class (class-of x))))
115 (string-downcase type)
116 (prin1-to-string type))))
118 (defmethod object-type-string ((x cons))
119 (if (listp (cdr x)) "list" "cons"))
121 (defmethod object-type-string ((x hash-table))
124 (defmethod object-type-string ((x condition))
127 (defmethod object-type-string ((x structure-object))
130 (defmethod object-type-string ((x standard-object))
133 (defmethod object-type-string ((x function))
135 (simple-fun "compiled function")
136 (closure "compiled closure")
138 (sb-eval:interpreted-function
139 "interpreted function")
143 "funcallable-instance")))
145 (defmethod object-type-string ((x stream))
148 (defmethod object-type-string ((x sb-gray:fundamental-stream))
151 (defmethod object-type-string ((x package))
154 (defmethod object-type-string ((x array))
155 (cond ((or (stringp x) (bit-vector-p x))
156 (format nil "~@[simple-~*~]~A"
157 (typep x 'simple-array)
159 (base-string "base-string")
163 (if (simple-vector-p x)
165 (format nil "~@[simple ~*~]~@[specialized ~*~]~:[array~;vector~]"
166 (typep x 'simple-array)
167 (neq t (array-element-type x))
170 (defmethod object-type-string ((x character))
172 (standard-char "standard-char")
173 (base-char "base-char")
176 (defun print-standard-describe-header (x stream)
177 (format stream "~&~A~% [~A]~%"
178 (object-self-string x)
179 (object-type-string x)))
181 (defgeneric describe-object (x stream))
185 (defmethod describe-object ((x t) s)
186 (print-standard-describe-header x s))
188 (defmethod describe-object ((x cons) s)
189 (print-standard-describe-header x s)
190 (describe-function x nil s))
192 (defmethod describe-object ((x function) s)
193 (print-standard-describe-header x s)
194 (describe-function nil x s))
196 (defmethod describe-object ((x class) s)
197 (print-standard-describe-header x s)
198 (describe-class nil x s)
199 (describe-instance x s))
201 (defmethod describe-object ((x sb-pcl::slot-object) s)
202 (print-standard-describe-header x s)
203 (describe-instance x s))
205 (defmethod describe-object ((x character) s)
206 (print-standard-describe-header x s)
207 (format s "~%:_Char-code: ~S" (char-code x))
208 (format s "~%:_Char-name: ~A~%_" (char-name x)))
210 (defmethod describe-object ((x array) s)
211 (print-standard-describe-header x s)
212 (format s "~%Element-type: ~S" (array-element-type x))
214 (if (array-has-fill-pointer-p x)
215 (format s "~%Fill-pointer: ~S~%Size: ~S"
217 (array-total-size x))
218 (format s "~%Length: ~S" (length x)))
219 (format s "~%Dimensions: ~S" (array-dimensions x)))
220 (let ((*print-array* nil))
221 (unless (typep x 'simple-array)
222 (format s "~%Adjustable: ~A" (if (adjustable-array-p x) "yes" "no"))
223 (multiple-value-bind (to offset) (array-displacement x)
224 (if (format s "~%Displaced-to: ~A~%Displaced-offset: ~S"
227 (format s "~%Displaced: no"))))
228 (when (and (not (array-displacement x)) (array-header-p x))
229 (format s "~%Storage vector: ~A"
230 (prin1-to-line (array-storage-vector x))))
233 (defmethod describe-object ((x hash-table) s)
234 (print-standard-describe-header x s)
235 ;; Don't print things which are already apparent from the printed
236 ;; representation -- COUNT, TEST, and WEAKNESS
237 (format s "~%Occupancy: ~,1F" (float (/ (hash-table-count x)
238 (hash-table-size x))))
239 (format s "~%Rehash-threshold: ~S" (hash-table-rehash-threshold x))
240 (format s "~%Rehash-size: ~S" (hash-table-rehash-size x))
241 (format s "~%Size: ~S" (hash-table-size x))
242 (format s "~%Synchronized: ~A" (if (hash-table-synchronized-p x) "yes" "no"))
245 (defmethod describe-object ((symbol symbol) stream)
246 (print-standard-describe-header symbol stream)
247 ;; Describe the value cell.
248 (let* ((kind (info :variable :kind symbol))
250 (:special "a special variable")
251 (:macro "a symbol macro")
252 (:constant "a constant variable")
253 (:global "a global variable")
254 (:unknown "an undefined variable")
255 (:alien "an alien variable"))))
256 (when (or (not (eq :unknown kind)) (boundp symbol))
257 (pprint-logical-block (stream nil)
258 (format stream "~@:_~A names ~A:" symbol wot)
259 (pprint-indent :block 2 stream)
260 (when (eq (info :variable :where-from symbol) :declared)
261 (format stream "~@:_Declared type: ~S"
262 (type-specifier (info :variable :type symbol))))
263 (when (info :variable :always-bound symbol)
264 (format stream "~@:_Declared always-bound."))
267 (let ((info (info :variable :alien-info symbol)))
268 (format stream "~@:_Value: ~S" (eval symbol))
269 (format stream "~@:_Type: ~S"
270 (sb-alien-internals:unparse-alien-type
271 (sb-alien::heap-alien-info-type info)))
272 (format stream "~@:_Address: #x~8,'0X"
273 (sap-int (eval (sb-alien::heap-alien-info-sap-form info))))))
275 (let ((expansion (info :variable :macro-expansion symbol)))
276 (format stream "~@:_Expansion: ~S" expansion)))
278 (format stream "~:@_Value: ~S" (symbol-value symbol)))
279 ((not (eq kind :unknown))
280 (format stream "~:@_Currently unbound.")))
281 (describe-documentation symbol 'variable stream)
284 ;; TODO: We could grovel over all packages looking for and
285 ;; reporting other phenomena, e.g. IMPORT and SHADOW, or
286 ;; availability in some package even after (SYMBOL-PACKAGE SYMBOL) has
289 ;; TODO: It might also be nice to describe (find-package symbol)
290 ;; if one exists. Maybe not all the exports, etc, but the package
292 (describe-function symbol nil stream)
293 (describe-class symbol nil stream)
296 (let* ((kind (info :type :kind symbol))
299 (or (info :type :expander symbol) t))
301 (or (info :type :translator symbol) t)))))
303 (pprint-newline :mandatory stream)
304 (pprint-logical-block (stream nil)
305 (format stream "~@:_~A names a ~@[primitive~* ~]type-specifier:"
307 (eq kind :primitive))
308 (pprint-indent :block 2 stream)
309 (describe-documentation symbol 'type stream (eq t fun))
311 (describe-lambda-list (if (eq :primitive kind)
312 (%fun-lambda-list fun)
313 (info :type :lambda-list symbol))
315 (when (eq (%fun-fun fun) (%fun-fun (constant-type-expander t)))
316 (format stream "~@:_Expansion: ~S" (funcall fun (list symbol))))))
319 (when (or (member symbol sb-c::*policy-qualities*)
320 (assoc symbol sb-c::*policy-dependent-qualities*))
321 (pprint-logical-block (stream nil)
322 (pprint-newline :mandatory stream)
323 (pprint-indent :block 2 stream)
324 (format stream "~A names a~:[ dependent~;n~] optimization policy quality:"
326 (member symbol sb-c::*policy-qualities*))
327 (describe-documentation symbol 'optimize stream t))
330 ;; Print out properties.
331 (let ((plist (symbol-plist symbol)))
333 (pprint-logical-block (stream nil)
334 (format stream "~%Symbol-plist:")
335 (pprint-indent :block 2 stream)
336 (sb-pcl::doplist (key value) plist
337 (format stream "~@:_~A -> ~A"
338 (prin1-to-line key :columns 2 :reserve 5)
339 (prin1-to-line value :columns 2 :reserve 5))))
342 (defmethod describe-object ((package package) stream)
343 (print-standard-describe-header package stream)
344 (pprint-logical-block (stream nil)
345 (describe-documentation package t stream)
346 (flet ((humanize (list)
347 (sort (mapcar (lambda (x)
354 (describe-stuff label list stream :escape nil)))
355 (let ((implemented (humanize (package-implemented-by-list package)))
356 (implements (humanize (package-implements-list package)))
357 (nicks (humanize (package-nicknames package)))
358 (uses (humanize (package-use-list package)))
359 (used (humanize (package-used-by-list package)))
360 (shadows (humanize (package-shadowing-symbols package)))
361 (this (list (package-name package)))
363 (do-external-symbols (ext package)
365 (setf exports (humanize exports))
366 (when (package-locked-p package)
367 (format stream "~@:_Locked."))
368 (when (set-difference implemented this :test #'string=)
369 (out "Implemented-by-list" implemented))
370 (when (set-difference implements this :test #'string=)
371 (out "Implements-list" implements))
372 (out "Nicknames" nicks)
373 (out "Use-list" uses)
374 (out "Used-by-list" used)
375 (out "Shadows" shadows)
376 (out "Exports" exports)
377 (format stream "~@:_~S internal symbols."
378 (package-internal-symbol-count package))))
381 ;;;; Helpers to deal with shared functionality
383 (defun describe-class (name class stream)
384 (let* ((by-name (not class))
385 (name (if class (class-name class) name))
386 (class (if class class (find-class name nil))))
388 (let ((metaclass-name (class-name (class-of class))))
389 (pprint-logical-block (stream nil)
391 (format stream "~@:_~A names the ~(~A~) ~S:"
395 (pprint-indent :block 2 stream))
396 (describe-documentation class t stream)
397 (when (sb-mop:class-finalized-p class)
398 (describe-stuff "Class precedence-list"
399 (mapcar #'class-name-or-class (sb-mop:class-precedence-list class))
401 (describe-stuff "Direct superclasses"
402 (mapcar #'class-name-or-class (sb-mop:class-direct-superclasses class))
404 (let ((subs (mapcar #'class-name-or-class (sb-mop:class-direct-subclasses class))))
406 (describe-stuff "Direct subclasses" subs stream)
407 (format stream "~@:_No subclasses.")))
408 (unless (sb-mop:class-finalized-p class)
409 (format stream "~@:_Not yet finalized."))
410 (if (eq 'structure-class metaclass-name)
411 (let* ((dd (find-defstruct-description name))
412 (slots (dd-slots dd)))
414 (format stream "~@:_Slots:~:{~@:_ ~S~
415 ~@:_ Type: ~A ~@[~A~]~
417 (mapcar (lambda (dsd)
421 (unless (eq t (dsd-raw-type dsd))
425 (format stream "~@:_No slots.")))
426 (let ((slots (sb-mop:class-direct-slots class)))
428 (format stream "~@:_Direct slots:~:{~@:_ ~S~
430 ~@[~@:_ Allocation: ~S~]~
431 ~@[~@:_ Initargs: ~{~S~^, ~}~]~
432 ~@[~@:_ Initform: ~S~]~
433 ~@[~@:_ Readers: ~{~S~^, ~}~]~
434 ~@[~@:_ Writers: ~{~S~^, ~}~]~
435 ~@[~@:_ Documentation:~@:_ ~@<~@;~A~:>~]~}"
436 (mapcar (lambda (slotd)
437 (list (sb-mop:slot-definition-name slotd)
438 (let ((type (sb-mop:slot-definition-type slotd)))
439 (unless (eq t type) type))
440 (let ((alloc (sb-mop:slot-definition-allocation slotd)))
441 (unless (eq :instance alloc) alloc))
442 (sb-mop:slot-definition-initargs slotd)
443 (sb-mop:slot-definition-initform slotd)
444 (sb-mop:slot-definition-readers slotd)
445 (sb-mop:slot-definition-writers slotd)
446 ;; FIXME: does this get the prefix right?
447 (quiet-doc slotd t)))
449 (format stream "~@:_No direct slots."))))
450 (pprint-indent :block 0 stream)
451 (pprint-newline :mandatory stream))))))
453 (defun describe-instance (object stream)
454 (let* ((class (class-of object))
455 (slotds (sb-mop:class-slots class))
456 (max-slot-name-length 0)
459 ;; Figure out a good width for the slot-name column.
460 (flet ((adjust-slot-name-length (name)
461 (setf max-slot-name-length
462 (max max-slot-name-length (length (symbol-name name))))))
463 (dolist (slotd slotds)
464 (adjust-slot-name-length (sb-mop:slot-definition-name slotd))
465 (push slotd (getf plist (sb-mop:slot-definition-allocation slotd))))
466 (setf max-slot-name-length (min (+ max-slot-name-length 3) 30)))
468 ;; Now that we know the width, we can print.
469 (flet ((describe-slot (name value)
470 (format stream "~% ~A~VT = ~A" name max-slot-name-length
471 (prin1-to-line value))))
472 (sb-pcl::doplist (allocation slots) plist
473 (format stream "~%Slots with ~S allocation:" allocation)
474 (dolist (slotd (nreverse slots))
476 (sb-mop:slot-definition-name slotd)
477 (sb-pcl::slot-value-or-default object (sb-mop:slot-definition-name slotd))))))
479 (format stream "~@:_No slots."))
482 (defun quiet-doc (object type)
483 (handler-bind ((warning #'muffle-warning))
484 (documentation object type)))
486 (defun describe-documentation (object type stream &optional undoc newline)
487 (let ((doc (quiet-doc object type)))
489 (format stream "~@:_Documentation:~@:_")
490 (pprint-logical-block (stream nil :per-line-prefix " ")
493 (format stream "~@:_(undocumented)")))
495 (pprint-newline :mandatory stream))))
497 (defun describe-stuff (label list stream &key (escape t))
500 (format stream "~@:_~A:~@<~;~{ ~S~^,~:_~}~;~:>" label list)
501 (format stream "~@:_~A:~@<~;~{ ~A~^,~:_~}~;~:>" label list))))
503 (defun describe-lambda-list (lambda-list stream)
504 (format stream "~@:_Lambda-list: ~:A" lambda-list))
506 (defun describe-function-source (function stream)
507 (if (compiled-function-p function)
508 (let* ((code (fun-code-header (%fun-fun function)))
509 (info (sb-kernel:%code-debug-info code)))
511 (let ((source (sb-c::debug-info-source info)))
513 (let ((namestring (sb-c::debug-source-namestring source)))
514 ;; This used to also report the times the source was created
515 ;; and compiled, but that seems more like noise than useful
516 ;; information -- but FWIW that are to be had as
517 ;; SB-C::DEBUG-SOUCE-CREATED/COMPILED.
519 (format stream "~@:_Source file: ~A" namestring))
520 ((sb-di:debug-source-form source)
521 (format stream "~@:_Source form:~@:_ ~S"
522 (sb-di:debug-source-form source)))))))))
524 (let ((source (sb-eval:interpreted-function-source-location function)))
526 (let ((namestring (sb-c:definition-source-location-namestring source)))
528 (format stream "~@:_Source file: ~A" namestring)))))))
530 (defun describe-function (name function stream)
531 (let ((name (if function (fun-name function) name)))
532 (if (not (or function (and (legal-fun-name-p name) (fboundp name))))
533 ;; Not defined, but possibly the type is declared, or we have
534 ;; compiled calls to it.
535 (when (legal-fun-name-p name)
536 (multiple-value-bind (from sure) (info :function :where-from name)
537 (when (or (eq :declared from) (and sure (eq :assumed from)))
538 (pprint-logical-block (stream nil)
539 (format stream "~%~A names an undefined function" name)
540 (pprint-indent :block 2 stream)
541 (format stream "~@:_~:(~A~) type: ~S"
543 (type-specifier (info :function :type name)))))))
545 (multiple-value-bind (fun what lambda-list ftype from inline
547 (cond ((and (not function) (symbolp name) (special-operator-p name))
548 (let ((fun (symbol-function name)))
549 (values fun "a special operator" (%fun-lambda-list fun))))
550 ((and (not function) (symbolp name) (macro-function name))
551 (let ((fun (macro-function name)))
552 (values fun "a macro" (%fun-lambda-list fun))))
554 (let ((fun (or function (fdefinition name))))
555 (multiple-value-bind (ftype from)
557 (values (%fun-type function) "Derived")
558 (let ((ctype (info :function :type name)))
559 (values (when ctype (type-specifier ctype))
561 ;; Ensure lazy pickup of information
563 (sb-c::maybe-update-info-for-gf name)
564 (ecase (info :function :where-from name)
565 (:declared "Declared")
566 ;; This is hopefully clearer to users
567 ((:defined-method :defined) "Derived"))))))
568 (if (typep fun 'generic-function)
571 (sb-mop:generic-function-lambda-list fun)
575 (or (sb-mop:generic-function-methods fun)
578 (if (compiled-function-p fun)
579 "a compiled function"
580 "an interpreted function")
581 (%fun-lambda-list fun)
586 (info :function :inlinep name)
587 (info :function :inline-expansion-designator name)))))))))
588 (pprint-logical-block (stream nil)
590 (format stream "~%~A names ~A:" name what)
591 (pprint-indent :block 2 stream))
592 (describe-lambda-list lambda-list stream)
593 (when (and ftype from)
594 (format stream "~@:_~A type: ~S" from ftype))
595 (describe-documentation name 'function stream)
597 (format stream "~@:_Inline proclamation: ~A (~:[no ~;~]inline expansion available)"
601 (format stream "~@:_Method-combination: ~S"
602 (sb-pcl::method-combination-type-name
603 (sb-pcl:generic-function-method-combination fun)))
604 (cond ((eq :none methods)
605 (format stream "~@:_No methods."))
607 (pprint-newline :mandatory stream)
608 (pprint-logical-block (stream nil)
609 (format stream "Methods:")
610 (dolist (method methods)
611 (pprint-indent :block 2 stream)
612 (format stream "~@:_(~A ~{~S ~}~:S)"
614 (method-qualifiers method)
615 (sb-pcl::unparse-specializers fun (sb-mop:method-specializers method)))
616 (pprint-indent :block 4 stream)
617 (describe-documentation method t stream nil))))))
618 (describe-function-source fun stream)
621 (awhen (and (legal-fun-name-p name) (compiler-macro-function name))
622 (pprint-logical-block (stream nil)
623 (format stream "~@:_~A has a compiler-macro:" name)
624 (pprint-indent :block 2 stream)
625 (describe-documentation it t stream)
626 (describe-function-source it stream))
628 (when (and (consp name) (eq 'setf (car name)) (not (cddr name)))
629 (let* ((name2 (second name))
630 (inverse (info :setf :inverse name2))
631 (expander (info :setf :expander name2)))
633 (pprint-logical-block (stream nil)
634 (format stream "~&~A has setf-expansion: ~S"
636 (pprint-indent :block 2 stream)
637 (describe-documentation name2 'setf stream))
640 (pprint-logical-block (stream nil)
641 (format stream "~&~A has a complex setf-expansion:"
643 (pprint-indent :block 2 stream)
644 (describe-lambda-list (%fun-lambda-list expander) stream)
645 (describe-documentation name2 'setf stream t)
646 (describe-function-source expander stream))
649 (describe-function `(setf ,name) nil stream))))