diff options
Diffstat (limited to 'lisp/disass.el')
-rw-r--r-- | lisp/disass.el | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/lisp/disass.el b/lisp/disass.el new file mode 100644 index 00000000000..77e5a7fc17d --- /dev/null +++ b/lisp/disass.el @@ -0,0 +1,446 @@ +;;; 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 object +returned by SYMBOL-FUNCTION. If OBJECT is not already compiled, we will +compile 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) + ]) + |