17.7 反汇编字节码

字节码并非由人工编写,而是交由字节编译器生成。 但我们提供了反汇编器以满足大家的好奇心—— 它能将字节编译后的代码转换为人类可读的形式。

字节码解释器的实现本质是一个简单的栈式虚拟机: 它先将值压入专属栈中,执行计算时再弹出这些值, 计算结果会重新压回栈内。 当字节码函数返回时,会从栈中弹出一个值作为函数的返回值。

除栈外,字节码函数还可通过在变量与栈之间传输值, 来使用、绑定和设置普通的 Lisp 变量。

Command: disassemble object &optional buffer-or-name

该命令展示 object 的反汇编代码: 交互调用时,或 buffer-or-namenil/省略时,输出至名为 *Disassemble* 的缓冲区; 若 buffer-or-namenil,则必须是缓冲区或已有缓冲区的名称,此时输出会插入到该缓冲区的光标位置,且光标停留在输出内容前方。

参数 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    ; Push factorial onto stack.
10  stack-ref 2           ; Push value of integer onto 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            ; Call current-time-string with no
                          ;   argument, push result onto stack as t1.
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 set n to the value.

;; (In effect, the sequence dup stack-set copies the top of
;; the stack into the value of n without 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 of t1 onto stack.
13  constant current-time-string  ; Push current-time-string
                                  ;   onto the top of the stack.
14  call     0            ; Call current-time-string again.
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.

emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike