📄 disass.el
字号:
;;; Disassembler for compiled Emacs Lisp code;; Copyright (C) 1986 Free Software Foundation;;; By Doug Cutting (doug@csli.stanford.edu);; This file is part of GNU Emacs.;; GNU Emacs is free software; you can redistribute it and/or modify;; it under the terms of the GNU General Public License as published by;; the Free Software Foundation; either version 1, or (at your option);; any later version.;; GNU Emacs is distributed in the hope that it will be useful,;; but WITHOUT ANY WARRANTY; without even the implied warranty of;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the;; GNU General Public License for more details.;; You should have received a copy of the GNU General Public License;; along with GNU Emacs; see the file COPYING. If not, write to;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.(require 'byte-compile "bytecomp")(defvar disassemble-column-1-indent 4 "*")(defvar disassemble-column-2-indent 9 "*")(defvar disassemble-recursive-indent 3 "*");(defun d (x); (interactive "xDiss "); (with-output-to-temp-buffer "*Disassemble*"; (disassemble-internal (list 'lambda '() x ''return-value); standard-output 0 t)))(defun disassemble (object &optional stream indent interactive-p) "Print disassembled code for OBJECT on (optional) STREAM.OBJECT can be a function name, lambda expression or any function objectreturned by SYMBOL-FUNCTION. If OBJECT is not already compiled, we willcompile it (but not redefine it)." (interactive (list (intern (completing-read "Disassemble function: " obarray 'fboundp t)) nil 0 t)) (or indent (setq indent 0)) ;Default indent to zero (if interactive-p (with-output-to-temp-buffer "*Disassemble*" (disassemble-internal object standard-output indent t)) (disassemble-internal object (or stream standard-output) indent nil)) nil)(defun disassemble-internal (obj stream indent interactive-p) (let ((macro 'nil) (name 'nil) (doc 'nil) args) (while (symbolp obj) (setq name obj obj (symbol-function obj))) (if (subrp obj) (error "Can't disassemble #<subr %s>" name)) (if (eq (car obj) 'macro) ;handle macros (setq macro t obj (cdr obj))) (if (not (eq (car obj) 'lambda)) (error "not a function")) (if (assq 'byte-code obj) nil (if interactive-p (message (if name "Compiling %s's definition..." "Compiling definition...") name)) (setq obj (byte-compile-lambda obj)) (if interactive-p (message "Done compiling. Disassembling..."))) (setq obj (cdr obj)) ;throw lambda away (setq args (car obj)) ;save arg list (setq obj (cdr obj)) (write-spaces indent stream) (princ (format "byte code%s%s%s:\n" (if (or macro name) " for" "") (if macro " macro" "") (if name (format " %s" name) "")) stream) (let ((doc (and (stringp (car obj)) (car obj)))) (if doc (progn (setq obj (cdr obj)) (write-spaces indent stream) (princ " doc: " stream) (princ doc stream) (terpri stream)))) (write-spaces indent stream) (princ " args: " stream) (prin1 args stream) (terpri stream) (let ((interactive (car (cdr (assq 'interactive obj))))) (if interactive (progn (write-spaces indent stream) (princ " interactive: " stream) (if (eq (car-safe interactive) 'byte-code) (disassemble-1 interactive stream (+ indent disassemble-recursive-indent)) (prin1 interactive stream) (terpri stream))))) (setq obj (assq 'byte-code obj)) ;obj is now call to byte-code (disassemble-1 obj stream indent)) (if interactive-p (message "")))(defun disassemble-1 (obj &optional stream indent) "Prints the byte-code call OBJ to (optional) STREAM.OBJ should be a call to BYTE-CODE generated by the byte compiler." (or indent (setq indent 0)) ;default indent to 0 (or stream (setq stream standard-output)) (let ((bytes (car (cdr obj))) ;the byte code (ptr -1) ;where we are in it (constants (car (cdr (cdr obj)))) ;constant vector ;(next-indent indent) offset tmp length) (setq length (length bytes)) (terpri stream) (while (< (setq ptr (1+ ptr)) length) ;(setq indent next-indent) (write-spaces indent stream) ;indent to recursive indent (princ (setq tmp (prin1-to-string ptr)) stream) ;print line # (write-char ?\ stream) (write-spaces (- disassemble-column-1-indent (length tmp) 1) stream) (setq op (aref bytes ptr)) ;fetch opcode ;; Note: as offsets are either encoded in opcodes or stored as ;; bytes in the code, this function (disassemble-offset) ;; can set OP and/or PTR. (setq offset (disassemble-offset));fetch offset (setq tmp (aref byte-code-vector op)) (if (consp tmp) (setq ;next-indent (if (numberp (cdr tmp)) ; (+ indent (cdr tmp)) ; (+ indent (funcall (cdr tmp) offset))) tmp (car tmp))) (setq tmp (symbol-name tmp)) (princ tmp stream) ;print op-name for opcode (if (null offset) nil (write-char ?\ stream) (write-spaces (- disassemble-column-2-indent (length tmp) 1) stream) ;indent to col 2 (princ ;print offset (cond ((or (eq op byte-varref) (eq op byte-varset) (eq op byte-varbind)) ;; it's a varname (atom) (aref constants offset)) ;fetch it from constants ((or (eq op byte-goto) (eq op byte-goto-if-nil) (eq op byte-goto-if-not-nil) (eq op byte-goto-if-nil-else-pop) (eq op byte-goto-if-not-nil-else-pop) (eq op byte-call) (eq op byte-unbind)) ;; it's a number offset) ;return it ((or (eq op byte-constant) (eq op byte-constant2)) ;; it's a constant (setq tmp (aref constants offset)) ;; but is constant byte code? (cond ((and (eq (car-safe tmp) 'lambda) (assq 'byte-code tmp)) (princ "<compiled lambda>" stream) (terpri stream) (disassemble ;recurse on compiled lambda tmp stream (+ indent disassemble-recursive-indent)) "") ((eq (car-safe tmp) 'byte-code) (princ "<byte code>" stream) (terpri stream) (disassemble-1 ;recurse on byte-code object tmp stream (+ indent disassemble-recursive-indent)) "") ((eq (car-safe (car-safe tmp)) 'byte-code) (princ "(<byte code>...)" stream) (terpri stream) (mapcar ;recurse on list of byte-code objects (function (lambda (obj) (disassemble-1 obj stream (+ indent disassemble-recursive-indent)))) tmp) "") ((and (eq tmp 'byte-code) (eq (aref bytes (+ ptr 4)) (+ byte-call 3))) ;; this won't catch cases where args are pushed w/ ;; constant2. (setq ptr (+ ptr 4)) "<compiled call to byte-code. compiled code compiled?>") (t ;; really just a constant (let ((print-escape-newlines t)) (prin1-to-string tmp))))) (t "<error in disassembler>")) stream)) (terpri stream))) nil)(defun disassemble-offset () "Don't call this!" ;; fetch and return the offset for the current opcode. ;; return NIL if this opcode has no offset ;; OP, PTR and BYTES are used and set dynamically (let (tem) (cond ((< op byte-nth) (setq tem (logand op 7)) (setq op (logand op 248)) (cond ((eq tem 6) (setq ptr (1+ ptr)) ;offset in next byte (aref bytes ptr)) ((eq tem 7) (setq ptr (1+ ptr)) ;offset in next 2 bytes (+ (aref bytes ptr) (progn (setq ptr (1+ ptr)) (lsh (aref bytes ptr) 8)))) (t tem))) ;offset was in opcode ((>= op byte-constant) (setq tem (- op byte-constant)) ;offset in opcode (setq op byte-constant) tem) ((or (= op byte-constant2) (and (>= op byte-goto) (<= op byte-goto-if-not-nil-else-pop))) (setq ptr (1+ ptr)) ;offset in next 2 bytes (+ (aref bytes ptr) (progn (setq ptr (1+ ptr)) (lsh (aref bytes ptr) 8)))) (t nil)))) ;no offset(defun write-spaces (n &optional stream) "Print N spaces to (optional) STREAM." (or stream (setq stream standard-output)) (if (< n 0) (setq n 0)) (if (eq stream (current-buffer)) (insert-char ?\ n) (while (> n 0) (write-char ?\ stream) (setq n (1- n)))))(defconst byte-code-vector '[<not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> (varref . 1) <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> (varset . -1) <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> (varbind . 0);Pops a value, "pushes" a binding <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> (call . -); #'-, not -1! <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> (unbind . -);"pops" bindings <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> (nth . -1) symbolp consp stringp listp (eq . -1) (memq . -1) not car cdr (cons . -1) list1 (list2 . -1) (list3 . -2) (list4 . -3) length (aref . -1) (aset . -2) symbol-value symbol-function (set . -1) (fset . -1) (get . -1) (substring . -2) (concat2 . -1) (concat3 . -2) (concat4 . -3) sub1 add1 (eqlsign . -1) ;= (gtr . -1) ;> (lss . -1) ;< (leq . -1) ;<= (geq . -1) ;>= (diff . -1) ;- negate ;unary - (plus . -1) ;+ (max . -1) (min . -1) <not-an-opcode> (point . 1) (mark\(obsolete\) . 1) goto-char insert (point-max . 1) (point-min . 1) char-after (following-char . 1) (preceding-char . 1) (current-column . 1) (indent-to . 1) (scan-buffer\(obsolete\) . -2) (eolp . 1) (eobp . 1) (bolp . 1) (bobp . 1) (current-buffer . 1) set-buffer (read-char . 1) set-mark\(obsolete\) interactive-p <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> (constant2 . 1) goto;>>> goto-if-nil;>> goto-if-not-nil;>> (goto-if-nil-else-pop . -1) (goto-if-not-nil-else-pop . -1) return (discard . -1) (dup . 1) (save-excursion . 1);Pushes a binding (save-window-excursion . 1);Pushes a binding (save-restriction . 1);Pushes a binding (catch . -1);Takes one argument, returns a value (unwind-protect . 1);Takes one argument, pushes a binding, returns a value (condition-case . -2);Takes three arguments, returns a value (temp-output-buffer-setup . -1) temp-output-buffer-show <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> <not-an-opcode> (constant . 1) ])
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -