バイトコードを記述することはなく、その作業はバイトコンパイラに任せられています。しかし、猫のような好奇心を満たすために、逆アセンブラを用意しています。この逆アセンブラは、バイトコンパイルされたコードを人間が読める形式に変換します。
バイトコードインタプリタは、シンプルなスタックマシンとして実装されています。独自のスタックに値をプッシュし、それらをポップして計算に使用し、その計算結果を再びスタックにプッシュします。バイトコード関数が戻る際は、スタックから値をポップし、それを関数の戻り値として返します。
スタックに加えて、バイトコード関数は、変数とスタックの間で値をやり取りすることで、通常のLisp変数の使用、バインド、および設定を行うことができます。
このコマンドは、object の逆アセンブルコードを表示します。対話モードでの使用時、または buffer-or-name が nil
であるか省略されている場合、出力は *Disassemble*
という名前のバッファに格納されます。buffer-or-name が nil
以外の場合、それはバッファであるか、既存のバッファ名である必要があります。その場合、出力はそこに格納され、point は出力前の位置に残されます。
引数 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 ; Get the value of integer and
; push it onto the stack.
1 constant 1 ; Push 1 onto stack.
2 eqlsign ; Pop top two values off stack, compare ; them, and push result onto stack.
3 goto-if-nil 1 ; Pop and test top of stack;
; if nil, go to 1, else continue.
6 constant 1 ; Push 1 onto top of stack.
7 return ; Return the top element of the stack.
8:1 dup ; Push value ofintegeronto stack. 9 constant factorial ; Pushfactorialonto stack. 10 stack-ref 2 ; Push value ofintegeronto stack. 11 sub1 ; Popinteger, decrement value, ; push new value onto stack. 12 call 1 ; Call functionfactorialusing first ; (i.e., top) stack element as argument; ; push returned value onto stack.
13 mult ; Pop top two values off stack, multiply ; them, and push result onto stack. 14 return ; Return the top element of the stack.
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 ; Push current-time-string
; onto top of stack.
1 call 0 ; Callcurrent-time-stringwith no ; argument, push result onto stack ast1.
2:1 stack-ref 1 ; Get value of the argument n
; and push the value on the stack.
3 sub1 ; Subtract 1 from top of stack.
4 dup ; Duplicate top of stack; i.e., copy the top ; of the stack and push copy onto stack. 5 stack-set 3 ; Pop the top of the stack, ; and setnto the value. ;; (In effect, the sequencedup stack-setcopies the top of ;; the stack into the value ofnwithout popping it.)
7 constant 0 ; Push 0 onto stack. 8 gtr ; Pop top two values off stack, ; test if n is greater than 0 ; and push result onto stack.
9 goto-if-not-nil 1 ; Goto 1 if n > 0
; (this continues the while loop)
; else continue.
12 dup ; Push value oft1onto stack. 13 constant current-time-string ; Pushcurrent-time-string; onto the top of the stack. 14 call 0 ; Callcurrent-time-stringagain.
15 list2 ; Pop top two elements off stack, create a ; list of them, and push it onto stack. 16 return ; Return value of the top of stack.