人はバイトコードを記述しません。それはバイトコンパイラーの仕事です。しかし好奇心を満たすために、わたしたちはディスアセンブラを提供しています。ディスアセンブラはバイトコードを人間が読めるフォームに変換します。
バイトコードインタープリターは、シンプルなスタックマシンとして実装されています。これは値を自身のスタックに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 ; スタックのトップの値をリターンする