17.7 逆アセンブルされたバイトコード

人はバイトコードを記述しません。それはバイトコンパイラーの仕事です。しかし好奇心を満たすために、わたしたちはディスアセンブラを提供しています。ディスアセンブラはバイトコードを人間が読めるフォームに変換します。

バイトコードインタープリターは、シンプルなスタックマシンとして実装されています。これは値を自身のスタックにpushして、計算で使用するためにそれらをpopして取り出し、その結果を再びそのスタックにpushして戻します。バイトコード関数がリターンするときは、スタックから値をpopして取り出し、その関数の値としてリターンします。

それに加えてスタックとバイトコード関数は、値を変数とスタック間で転送することにより、普通のLisp変数を使用したり、バインドやセットを行うことができます。

Command: disassemble object &optional buffer-or-name

このコマンドはobjectにたいするディスアセンブルされたコードを表示する。インタラクティブに使用した場合、またはbuffer-or-namenilか省略された場合は、*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                ; スタックのトップの値をリターンする

This page has generated for branch:work/emacs-30_69b16e5c63840479270d32f58daea923fe725b90, commit:5e3f74b56ff47b5bcef2526c70f53f749bbd45f6 to check Japanese translation.