通常プログラムを書いていると、関数を書いて、その関数を呼び出す関数を書いて、というような木構造とか階層構造とか複雑になっていく。 これを辿る方法について。
実行環境はlemのslime上で、ccl 1.12.1が前半でsbcl 2.2.9が後半。
slimeの挙動
slimeには M-.
で関数定義にジャンプする機能があるが、これは swank
の find-definitions-for-emacs
により実現されており、repl上からも使える。
(swank:find-definitions-for-emacs "mapcar")
(("#'MAPCAR" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/lib/lists.lisp") (:POSITION 26761) NIL)) ("(COMPILER-MACRO MAPCAR)" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/compiler/optimizers.lisp") (:POSITION 33286) NIL)))
mapcar
という名前だけからは複数の該当個所が出てくる。
逆に M-_
で関数利用先は swank の xrefs
。
(swank:xrefs '(:calls :macroexpands :binds :references :sets :specializes) "mapcar")
((:CALLS ("#'UIOP/UTILITY:WHILE-COLLECTING" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/tools/asdf.lisp") (:POSITION 57642) (:SNIPPET "(defmacro while-collecting ((&rest colle"))) ("#'SWANK::MAP-IF" (:LOCATION (:FILE "/home/gos-k/.roswell/lisp/quicklisp/dists/quicklisp/software/slime-v2.27/swank.lisp") (:POSITION 105172) (:SNIPPET "(defun map-if (test fn &rest lists) \"L")) ) ("#'SWANK::SCORE-COMPLETION" (:LOCATION (:FILE "/home/gos-k/.roswell/lisp/quicklisp/dists/quicklisp/software/slime-v2.27/contrib/swank-fuzzy.lisp") (:POSITION 29566) (:SNIPPET "(defun score-completion (completion shor"))) ("#'CCL::FIXED-VALUES-OP" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/level-1/l1-typesys.lisp") (:POSITION 32586) NIL)) ("(:METHOD CCL::FORCE-BREAK-IN-LISTENER (#<STANDARD-CLASS PROCESS>))" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/level-1/l1-events.lisp") (:POSITION 3880) NIL)) ("#'CCL::DECOMP-LAMBDA-LIST" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/compiler/nx-basic.lisp") (:POSITION 43172) NIL)) ("#'CCL::NX1-TYPESPEC-FOR-TYPEP" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/compiler/nx1.lisp") (:POSITION 2900) NIL)) ("#'CCL::DEFAULT-SETF" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/lib/macros.lisp") (:POSITION 26221) NIL)) ("#'RESTART-BIND" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/lib/macros.lisp") (:POSITION 8651) NIL)) ("#'CCL::BITOPF" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/lib/macros.lisp") (:POSITION 52042) NIL)) ("#'CCL::LONG-FORM-DEFINE-METHOD-COMBINATION" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/lib/method-combination.lisp") (:POSITION 24944) NIL)) ("#'CCL::REFERENCE-METHOD" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/lib/macros.lisp") (:POSITION 126784) NIL)) ("#'POP" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/lib/setf.lisp") (:POSITION 30800) NIL)) ("#'PUSHNEW" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/lib/setf.lisp") (:POSITION 30051) NIL)) ("#'PUSH" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/lib/setf.lisp") (:POSITION 29432) NIL)) ("#'DECF" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/lib/setf.lisp") (:POSITION 14220) NIL)) ("#'INCF" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/lib/setf.lisp") (:POSITION 13504) NIL)) ("#'DEFSETF" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/lib/setf.lisp") (:POSITION 8755) NIL)) ("#'CCL::DEFSTRUCT-BOA-CONSTRUCTOR" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/lib/defstruct-lds.lisp") (:POSITION 14720) NIL)) ("#'CCL::%TEMP-PUSH" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/compiler/nxenv.lisp") (:POSITION 37263) NIL)) ("#'CCL::TRACE-PRINT-BODY" (:LOCATION (:FILE "/home/gos-k/.roswell/impls/x86-64/linux/ccl-bin/1.12.1/lib/encapsulate.lisp") (:POSITION 11734) NIL))))
これらはテキストエディタを前提としてるので、カーソル位置から名前を取り、それによって検索している。
欲しいものに近いがちょっと違って、指定した関数の中で呼び出されている関数のリストが欲しい。
呼び出している関数のリスト
xrefs
に :callees
を渡すと取れるみたいだが、cclだと動かなかったのでsbclで試す。
repl上で2つ関数定義する。
(defun alfa () (pprint :alfa)) (defun bravo () (alfa))
bravoを指定する。
(swank:xrefs '(:callees) "bravo")
呼び出してるalfaが取れる。 repl上で定義してるのでソースファイルがないエラーになってる。
((:CALLEES ("ALFA" (:ERROR "Error: DEFINITION-SOURCE of function ALFA did not contain meaningful information."))))
alfaを指定する。
(swank:xrefs '(:callees) "alfa")
呼び出しているpprintが取れる。
((:CALLEES ("PPRINT" (:LOCATION (:FILE "/home/gos-k/.roswell/src/sbcl-2.2.9/src/code/print.lisp") (:POSITION 6690) (:SNIPPET "(defun pprint (object &optional stream) \"Prettily output OBJECT preceded by a newline.\" (declare (explicit-check)) (let ((*print-pretty* t) (*print-escape* t) (stream (out-stream-from-designator stream))) (terpri stream) (outp")))))
これを再帰的に行うと、
(defun trace-functions (name) (pprint name) (let* ((refs (swank:xrefs '(:callees) name)) (callees (last (first refs)))) (dolist (callee callees) (trace-functions (first callee))))) (trace-functions "bravo")
"bravo" "ALFA" "PPRINT" "TERPRI"
多分関数が辿れた。
ついでに xrefs
自体も辿ってみる。
(trace-functions "swank:xrefs")
"swank:xrefs" "SWANK:XREF" "SWANK:FROM-STRING" "SWANK::CALL-WITH-BUFFER-SYNTAX" "SWANK/BACKEND:CALL-WITH-SYNTAX-HOOKS" "SB-IMPL::GET3" "SYMBOL-PLIST"