Final version works off DSPECs
This is my final version... it works off of DSPEC's which permits
things like exploring from methods with particular signatures.
;; call-path-checker.lisp -- A visual aid to determining if one
function can be reached from another
;;
;; DM/RAL 08/09
;;
--------------------------------------------------------------------------------
;; Feel free to use, modify, or whatever...
;;
;; Use as:
;; 1) First load in your code that you want to explore
;;
;; 2) Enter: (path-between fn1 fn2) where path runs from fn1 to fn2
;; and both must be dspecs for functions or methods:
;; e.g., (path-between 'connect-to-database 'rollback)
;; or (path-between '(method deref (oid)) 'fetch-from-logfile)
;;
;; 3) If there are one or more paths, they will be displayed in a
graph pane
;; Otherwise, the returned value is NIL.
;;
--------------------------------------------------------------------------------
(in-package :user)
(defun view-tree (tree from to)
(when tree
(capi:contain
(make-instance
'capi:graph-pane
:title (format nil "Path from ~A to ~A" from to)
:roots (list tree)
:children-function #'cdr
:print-function #'(lambda (node)
(format nil "~A"
(let ((ds (car node)))
(case (car ds)
(function (cadr ds))
(method ds)))))
:edge-pinboard-class 'capi:arrow-pinboard-object
:interaction :single-selection
:action-callback 'graph-pane-action-callback
)
:best-width 400
:best-height 300)))
(defun graph-pane-action-callback (node intf)
(declare (ignore intf))
(ignore-errors
(ed (car node))))
(defun path-between (dspec1 dspec2)
;; dspec1 and dspec2 are approximate dspecs
;; e.g., (path-between 'deref 'fetch-from-logfile)
;; or (path-between '(method deref (oid)) 'fetch-from-logfile)
(let ((ds1 (dspec:canonicalize-dspec dspec1))
(ds2 (dspec:canonicalize-dspec dspec2))
(seen (make-hash-table :test #'dspec:dspec-equal)))
(labels ((dspec-type (ds)
(case (car ds)
(function
(ignore-errors ;; produce NIL on error, which will
just skip it
(if (typep (fdefinition (cadr ds)) 'standard-
generic-function)
'generic-function
'function)))
(method 'method)))
(make-method-dspec (ds meth)
(dspec:canonicalize-dspec
`(method ,(cadr ds)
,@(method-qualifiers meth)
,(mapcar (lambda (spec)
(if (consp spec)
spec
(class-name spec)))
(method-specializers meth))))) )
(view-tree
(um:nlet iter ((ds ds1))
(if (dspec:dspec-equal ds ds2)
(list ds)
(let ((found nil))
(setf (gethash ds seen) t)
(case (dspec-type ds)
(generic-function
(let* ((fn (fdefinition (cadr ds)))
(methods (clos:generic-function-methods fn)))
(dolist (meth methods)
(let ((meth-ds (make-method-dspec ds meth)))
(unless (gethash meth-ds seen)
(um:when-let (path (iter meth-ds))
(push path found)))))))
((function method)
(dolist (sub-fn (calls-who ds))
(let ((sub-ds (dspec:canonicalize-dspec sub-fn)))
(unless (gethash sub-ds seen)
(um:when-let (path (iter sub-ds))
(push path found)))))) )
(when found
(cons ds (nreverse found))) )))
dspec1 dspec2))))
Dr. David McClain
dbm@refined-audiometrics.com