home *** CD-ROM | disk | FTP | other *** search
- ;;; 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)
- ])
-
-