+;;; This is here as a discussion point, not yet a supported interface. If
+;;; you would like to use the functions here, or you would like other
+;;; functions to be here, join the debate on sbcl-devel
+
+;;; For the avoidance of doubt, the exported interface is the
+;;; proposed supported interface.
+
(defpackage :sb-introspect
(:use "CL")
- (:export "FUNCTION-ARGLIST" "VALID-FUNCTION-NAME-P"))
-
+ (:export "FUNCTION-ARGLIST" "VALID-FUNCTION-NAME-P"
+ "FIND-DEFINITION-SOURCE"
+ "DEFINITION-SOURCE" "DEFINITION-SOURCE-PATHNAME"
+ "DEFINITION-NOT-FOUND" "DEFINITION-NAME"
+ "DEFINITION-SOURCE-FORM-NUMBER" ; unsure. character offset instead?
+ ))
(in-package :sb-introspect)
-;; This is here as a discussion point, not yet a supported interface. If
-;; you would like to use the functions here, or you would like other
-;; functions to be here, join the debate on sbcl-devel
(defun valid-function-name-p (name)
"True if NAME denotes a function name that can be passed to MACRO-FUNCTION or FDEFINITION "
(and (sb-int:valid-function-name-p name) t))
(defun function-arglist (function)
- "Given a function designator FUNCTION, return a description of its lambda list. Works for macros, simple functions and generic functions"
+ "Describe the lambda list for the function designator FUNCTION.
+Works for macros, simple functions and generic functions"
(cond ((valid-function-name-p function)
(function-arglist
(or (macro-function function) (fdefinition function))))
(t
(sb-impl::%simple-fun-arglist function))))
+;;; Considering whether to throw this or something like it when a definition
+;;; is unforthcoming. Presently we do something undefined (NIL or random
+;;; error)
+(define-condition definition-not-found (error)
+ ((name :initarg :name :reader definition-name))
+ (:report (lambda (c s)
+ (format s "No definition for ~S known" (definition-name c)))))
+
+;;; find-definition-source returns a definition-source object, with accessors
+;;; as per export list. Might not be a struct.
+(defstruct definition-source pathname form-number)
+
+
+;;; the intention is that everything we're able to query the source
+;;; location for, we should be able to do it through this gf
+(defgeneric find-definition-source (thing))
+
+;;; breaks on structure accessors, probably other closures as well
+(defmethod find-definition-source ((o function))
+ (let* ((name (sb-vm::%simple-fun-name o))
+ (debug-info
+ (sb-kernel:%code-debug-info (sb-kernel:fun-code-header o)))
+ (debug-source (car (sb-c::compiled-debug-info-source debug-info))))
+ ;; FIXME why only the first debug-source? can there be >1?
+ (sb-int:aver (not (cdr (sb-c::compiled-debug-info-source debug-info))))
+ (make-definition-source
+ :pathname
+ (and (eql (sb-c::debug-source-from debug-source) :file)
+ (parse-namestring (sb-c::debug-source-name debug-source)))
+ :form-number
+ (loop for debug-fun across (sb-c::compiled-debug-info-fun-map debug-info)
+ when (and (sb-c::debug-fun-p debug-fun)
+ (eql (sb-c::compiled-debug-fun-name debug-fun) name))
+ return (sb-c::compiled-debug-fun-tlf-number debug-fun)))))
+
+(defmethod find-definition-source ((o method))
+ (find-definition-source (or (sb-pcl::method-fast-function o)
+ (sb-pcl:method-function o))))
+
+(defmethod find-definition-source (name)
+ (and (valid-function-name-p name)
+ (find-definition-source (or (macro-function name) (fdefinition name)))))
+
--- /dev/null
+(defpackage :sb-introspect-test
+ (:use "SB-INTROSPECT" "CL"))
+(load (compile-file (merge-pathnames "test.lisp" *load-pathname*)))
+
+(assert (equal (function-arglist 'cl-user::one)
+ '(cl-user::a cl-user::b cl-user::c)))
+
+(defun matchp (object form-number)
+ (let ((ds (sb-introspect:find-definition-source object)))
+ (and (pathnamep (sb-introspect:definition-source-pathname ds))
+ (= form-number (sb-introspect:definition-source-form-number ds)))))
+
+(assert (matchp 'cl-user::one 2))
+(assert (matchp #'cl-user::one 2))
+; (assert (matchp 'two 2)) ; defgenerics don't work yet
+(assert (matchp (car (sb-pcl:generic-function-methods #'cl-user::two)) 4))