Люди не пишут байт-код; эта задача остается за байт-компилятором. Но мы предоставляем декомпилятор, чтобы удовлетворить кошачью любознательность. Декомпилятор преобразует байт-скомпилированный код в удобочитаемый человеком вид.
Интерпретатор байт-кода реализован как простая стековая машина. Он помещает значения в собственный стек, а затем извлекает их для использования в вычислениях, результаты которых сами помещаются обратно в стек. Когда функция байт-кода возвращается, она извлекает значение из стека и возвращает его как значение функции.
В дополнение к стеку, функции байт-кода могут использовать, связывать и устанавливать обычные переменные Lisp, передавая значения между переменными и стеком.
Эта команда отображает декомпилированный код для object. При интерактивном использовании или если buffer-or-name равен nil или опущен, вывод помещается в буфер с именем *Disassemble*. Если buffer-or-name не равен nil, он должен быть буфером или именем существующего буфера. Тогда вывод помещается туда, в точку, и точка остается перед выводом.
Аргумент object может быть именем функции, лямбда-выражением (см. Лямбда-выражения) или объектом байт-кода (см. Объекты функций замыкания). Если это лямбда-выражение, disassemble компилирует его и декомпилирует полученный скомпилированный код.
Вот два примера использования функции disassemble. Мы добавили пояснительные комментарии, чтобы помочь вам соотнести байт-код с исходным кодом Lisp; они не появляются в выводе disassemble.
(defun factorial (integer)
"Вычислить факториал целого числа."
(if (= 1 integer) 1
(* integer (factorial (1- integer)))))
⇒ factorial
(factorial 4)
⇒ 24
(disassemble 'factorial)
-| байт-код для factorial:
doc: Вычислить факториал целого числа.
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 ; Поместитьfactorialв стек. 10 stack-ref 2 ; Поместить значениеintegerв стек. 11 sub1 ; Извлечьinteger, уменьшить значение, ; поместить новое значение в стек. 12 call 1 ; Вызвать функциюfactorial, используя первый ; (т.е. верхний) элемент стека в качестве аргумента; ; поместить возвращенное значение в стек.
13 mult ; Извлечь две верхние значения из стека, умножить ; их и поместить результат в стек. 14 return ; Вернуть верхний элемент стека.
Функция silly-loop несколько сложнее:
(defun silly-loop (n)
"Вернуть время до и после N итераций цикла."
(let ((t1 (current-time-string)))
(while (> (setq n (1- n))
0))
(list t1 (current-time-string))))
⇒ silly-loop
(disassemble 'silly-loop)
-| байт-код для silly-loop:
doc: Вернуть время до и после N итераций цикла.
args: (arg1)
0 constant current-time-string ; Поместить current-time-string в стек.
; в верхний элемент стека.
1 call 0 ; Вызватьcurrent-time-stringбез аргументов, ; поместить результат в стек какt1.
2:1 stack-ref 1 ; Получить значение аргумента n
; и поместить его в стек.
3 sub1 ; Вычесть 1 из верхнего элемента стека.
4 dup ; Дублировать верхний элемент стека; т.е. скопировать верхний ; элемент стека и поместить копию в стек. 5 stack-set 3 ; Извлечь верхний элемент стека, ; и установитьnравным этому значению. ;; (По сути, последовательностьdup stack-setкопирует верхний элемент ;; стека в значениеnбез его извлечения.)
7 constant 0 ; Поместить 0 в стек. 8 gtr ; Извлечь две верхние значения из стека, ; проверить, больше ли n 0 ; и поместить результат в стек.
9 goto-if-not-nil 1 ; Перейти к 1, если n > 0
; (это продолжает цикл while)
; иначе продолжить.
12 dup ; Поместить значениеt1в стек. 13 constant current-time-string ; Поместитьcurrent-time-stringв стек. ; в верхний элемент стека. 14 call 0 ; Снова вызватьcurrent-time-string.
15 list2 ; Извлечь две верхние элемента из стека, создать ; из них список и поместить его в стек. 16 return ; Вернуть значение верхнего элемента стека.