人はバイトコードを記述しません。それはバイトコンパイラーの仕事です。しかし好奇心を満たすために、わたしたちはディスアセンブラを提供しています。ディスアセンブラはバイトコードを人間が読めるフォームに変換します。
バイトコードインタープリターは、シンプルなスタックマシンとして実装されています。これは値を自身のスタックにpushして、計算で使用するためにそれらをpopして取り出し、その結果を再びそのスタックにpushして戻します。バイトコード関数がリターンするときは、スタックから値をpopして取り出し、その関数の値としてリターンします。
それに加えてスタックとバイトコード関数は、値を変数とスタック間で転送することにより、普通のLisp変数を使用したり、バインドやセットを行うことができます。
このコマンドはobjectにたいするディスアセンブルされたコードを表示する。インタラクティブに使用した場合、またはbuffer-or-nameがnilか省略された場合は、*Disassemble*という名前のバッファーに出力します。buffer-or-nameが非nilなら、それはバッファーもしくは既存のバッファーの名前でなければならない。その場合は、そのバッファーのポイント位置に出力され、ポイントは出力の前に残りされる。
引数objectには関数名、ラムダ式(ラムダ式を参照)、またはバイトコードオブジェクト(クロージャ関数オブジェクトを参照)を指定できる。ラムダ式ならdisassembleはそれをコンパイルしてから、そのコンパイル済みコードをディスアセンブルする。
以下にdisassemble関数を使用した例を2つ示します。バイトコードとLispソースを関連付ける助けとなるように、説明的なコメントを追加してあります。これらのコメントはdisassembleの出力にはありません。
(defun factorial (integer)
"Compute factorial of an integer."
(if (= 1 integer) 1
(* integer (factorial (1- integer)))))
⇒ factorial
(factorial 4)
⇒ 24
(disassemble 'factorial)
-| byte-code for factorial:
doc: Compute factorial of an integer.
args: (arg1)
0 dup ; integerの値を取得して
; それをスタック上にpushする
1 constant 1 ; スタック上に1をpushする
2 eqlsign ; 2つの値をスタックからpopして取り出し、 ; それらを比較して結果をスタック上にpushする
3 goto-if-nil 1 ; スタックのトップをpopしてテストする
; nilなら1へ、それ以外はcontinue
6 constant 1 ; スタックのトップに1をpushする
7 return ; スタックのトップの要素をリターンする
8:1 dup ;integerの値をスタック上にpushする 9 constant factorial ;factorialをスタック上にpushする 10 stack-ref 2 ;integerの値をスタック上にpushする 11 sub1 ;integerをpopして値をデクリメントする ; スタック上に新しい値をpushする 12 call 1 ; スタックの最初(トップ)の要素を引数として ; 関数factorialを呼び出す ; リターン値をスタック上にpushする
13 mult ; スタックのトップ2要素をpopして取り出し乗じ ; 結果をスタック上にpushする 14 return ; スタックのトップ要素をリターンする
silly-loopは幾分複雑です:
(defun silly-loop (n)
"Return time before and after N iterations of a loop."
(let ((t1 (current-time-string)))
(while (> (setq n (1- n))
0))
(list t1 (current-time-string))))
⇒ silly-loop
(disassemble 'silly-loop)
-| byte-code for silly-loop:
doc: Return time before and after N iterations of a loop.
args: (arg1)
0 constant current-time-string ; current-time-stringを
; スタック上のトップにpushする
1 call 0 ; 引数なしでcurrent-time-stringを呼び出して ; 結果をt1のようにスタック上にpushする
2:1 stack-ref 1 ; 引数nの値を取得して
; その値をスタック上にpushする
3 sub1 ; スタックのトップから1を減ずる
4 dup ; スタックのトップを複製する ; つまりスタックのトップをコピーしてスタック上にpushする 5 stack-set 3 ; ; スタックのトップをpopして ;nにその値をセットする ;; (要はシーケンスdup stack-setはpopせずに ;; スタックのトップをnの値にコピーする)
7 constant 0 ; スタック上に0をpushする 8 gtr ; スタックのトップ2値をpopして取り出し ; nが0より大かテストし ; 結果をスタック上にpushする
9 goto-if-not-nil 1 ; n > 0なら1へ
; (これはwhile-loopを継続する)
; それ以外はcontinue
12 dup ;t1の値をスタック上にpushする 13 constant current-time-string ;current-time-stringを ; スタックのトップにpushする 14 call 0 ; 再度current-time-stringを呼び出す
15 list2 ; スタックのトップ2要素をpopして取り出し ; それらのリストを作りスタック上にpushする 16 return ; スタックのトップの値をリターンする