字节码并非由人工编写,而是交由字节编译器生成。 但我们提供了反汇编器以满足大家的好奇心—— 它能将字节编译后的代码转换为人类可读的形式。
字节码解释器的实现本质是一个简单的栈式虚拟机: 它先将值压入专属栈中,执行计算时再弹出这些值, 计算结果会重新压回栈内。 当字节码函数返回时,会从栈中弹出一个值作为函数的返回值。
除栈外,字节码函数还可通过在变量与栈之间传输值, 来使用、绑定和设置普通的 Lisp 变量。
该命令展示 object 的反汇编代码:
交互调用时,或 buffer-or-name 为 nil/省略时,输出至名为 *Disassemble* 的缓冲区;
若 buffer-or-name 非 nil,则必须是缓冲区或已有缓冲区的名称,此时输出会插入到该缓冲区的光标位置,且光标停留在输出内容前方。
参数 object 可以是:函数名、lambda 表达式(see Lambda 表达式)、字节码对象(see 闭包函数对象)
若为 lambda 表达式,disassemble 会先编译该表达式,再对编译后的代码进行反汇编。
以下是两个使用 disassemble 函数的示例。
我们添加了注释来帮助你关联字节码与 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 的值并
; 压入栈中。
1 constant 1 ; 将 1 压入栈。
2 eqlsign ; 弹出栈顶两个值进行比较, ; 并将比较结果压入栈。
3 goto-if-nil 1 ; 弹出栈顶值并测试;
; 若为 nil,跳转到标号 1,否则继续执行。
6 constant 1 ; 将 1 压入栈顶。
7 return ; 返回栈顶元素。
8:1 dup ; 将integer的值压入栈。 9 constant factorial ; Pushfactorialonto stack. 10 stack-ref 2 ; Push value ofintegeronto stack. 11 sub1 ; 弹出integer,将其值减 1, ; 并将新值压入栈。 12 call 1 ; 调用函数factorial,以栈顶元素为参数; ; 将返回值压入栈。.
13 mult ; 弹出栈顶两个值相乘, ; 并将乘积压入栈。 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 ; 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.