|
|
1.1 root 1: ;;; Disassembler for compiled Emacs Lisp code
2: ;; Copyright (C) 1986 Free Software Foundation
3: ;;; By Doug Cutting ([email protected])
4:
5: ;; This file is part of GNU Emacs.
6:
7: ;; GNU Emacs is distributed in the hope that it will be useful,
8: ;; but WITHOUT ANY WARRANTY. No author or distributor
9: ;; accepts responsibility to anyone for the consequences of using it
10: ;; or for whether it serves any particular purpose or works at all,
11: ;; unless he says so in writing. Refer to the GNU Emacs General Public
12: ;; License for full details.
13:
14: ;; Everyone is granted permission to copy, modify and redistribute
15: ;; GNU Emacs, but only under the conditions described in the
16: ;; GNU Emacs General Public License. A copy of this license is
17: ;; supposed to have been given to you along with GNU Emacs so you
18: ;; can know your rights and responsibilities. It should be in a
19: ;; file named COPYING. Among other things, the copyright notice
20: ;; and this notice must be preserved on all copies.
21:
22:
23: (require 'byte-compile "bytecomp")
24:
25: (defvar disassemble-column-1-indent 4 "*")
26:
27: (defvar disassemble-column-2-indent 9 "*")
28:
29: (defvar disassemble-recursive-indent 3 "*")
30:
31: ;(defun d (x)
32: ; (interactive "xDiss ")
33: ; (with-output-to-temp-buffer "*Disassemble*"
34: ; (disassemble-internal (list 'lambda '() x ''return-value)
35: ; standard-output 0 t)))
36:
37: (defun disassemble (object &optional stream indent interactive-p)
38: "Print disassembled code for OBJECT on (optional) STREAM.
39: OBJECT can be a function name, lambda expression or any function object
40: returned by SYMBOL-FUNCTION. If OBJECT is not already compiled, we will
41: compile it (but not redefine it)."
42: (interactive (list (intern (completing-read "Disassemble function: "
43: obarray 'fboundp t))
44: nil 0 t))
45: (or indent (setq indent 0)) ;Default indent to zero
46: (if interactive-p
47: (with-output-to-temp-buffer "*Disassemble*"
48: (disassemble-internal object standard-output indent t))
49: (disassemble-internal object (or stream standard-output) indent nil))
50: nil)
51:
52: (defun disassemble-internal (obj stream indent interactive-p)
53: (let ((macro 'nil)
54: (name 'nil)
55: (doc 'nil)
56: args)
57: (while (symbolp obj)
58: (setq name obj
59: obj (symbol-function obj)))
60: (if (subrp obj)
61: (error "Can't disassemble #<subr %s>" name))
62: (if (eq (car obj) 'macro) ;handle macros
63: (setq macro t
64: obj (cdr obj)))
65: (if (not (eq (car obj) 'lambda))
66: (error "not a function"))
67: (if (assq 'byte-code obj)
68: nil
69: (if interactive-p (message (if name
70: "Compiling %s's definition..."
71: "Compiling definition...")
72: name))
73: (setq obj (byte-compile-lambda obj))
74: (if interactive-p (message "Done compiling. Disassembling...")))
75: (setq obj (cdr obj)) ;throw lambda away
76: (setq args (car obj)) ;save arg list
77: (setq obj (cdr obj))
78: (write-spaces indent stream)
79: (princ (format "byte code%s%s%s:\n"
80: (if (or macro name) " for" "")
81: (if macro " macro" "")
82: (if name (format " %s" name) ""))
83: stream)
84: (let ((doc (and (stringp (car obj)) (car obj))))
85: (if doc
86: (progn (setq obj (cdr obj))
87: (write-spaces indent stream)
88: (princ " doc: " stream)
89: (princ doc stream)
90: (terpri stream))))
91: (write-spaces indent stream)
92: (princ " args: " stream)
93: (prin1 args stream)
94: (terpri stream)
95: (let ((interactive (car (cdr (assq 'interactive obj)))))
96: (if interactive
97: (progn (write-spaces indent stream)
98: (princ " interactive: " stream)
99: (if (eq (car-safe interactive) 'byte-code)
100: (disassemble-1 interactive stream
101: (+ indent disassemble-recursive-indent))
102: (prin1 interactive stream)
103: (terpri stream)))))
104: (setq obj (assq 'byte-code obj)) ;obj is now call to byte-code
105: (disassemble-1 obj stream indent))
106: (if interactive-p
107: (message "")))
108:
109: (defun disassemble-1 (obj &optional stream indent)
110: "Prints the byte-code call OBJ to (optional) STREAM.
111: OBJ should be a call to BYTE-CODE generated by the byte compiler."
112: (or indent (setq indent 0)) ;default indent to 0
113: (or stream (setq stream standard-output))
114: (let ((bytes (car (cdr obj))) ;the byte code
115: (ptr -1) ;where we are in it
116: (constants (car (cdr (cdr obj)))) ;constant vector
117: ;(next-indent indent)
118: offset tmp length)
119: (setq length (length bytes))
120: (terpri stream)
121: (while (< (setq ptr (1+ ptr)) length)
122: ;(setq indent next-indent)
123: (write-spaces indent stream) ;indent to recursive indent
124: (princ (setq tmp (prin1-to-string ptr)) stream) ;print line #
125: (write-char ?\ stream)
126: (write-spaces (- disassemble-column-1-indent (length tmp) 1)
127: stream)
128: (setq op (aref bytes ptr)) ;fetch opcode
129: ;; Note: as offsets are either encoded in opcodes or stored as
130: ;; bytes in the code, this function (disassemble-offset)
131: ;; can set OP and/or PTR.
132: (setq offset (disassemble-offset));fetch offset
133: (setq tmp (aref byte-code-vector op))
134: (if (consp tmp)
135: (setq ;next-indent (if (numberp (cdr tmp))
136: ; (+ indent (cdr tmp))
137: ; (+ indent (funcall (cdr tmp) offset)))
138: tmp (car tmp)))
139: (setq tmp (symbol-name tmp))
140: (princ tmp stream) ;print op-name for opcode
141: (if (null offset)
142: nil
143: (write-char ?\ stream)
144: (write-spaces (- disassemble-column-2-indent (length tmp) 1)
145: stream) ;indent to col 2
146: (princ ;print offset
147: (cond ((or (eq op byte-varref)
148: (eq op byte-varset)
149: (eq op byte-varbind))
150: ;; it's a varname (atom)
151: (aref constants offset)) ;fetch it from constants
152: ((or (eq op byte-goto)
153: (eq op byte-goto-if-nil)
154: (eq op byte-goto-if-not-nil)
155: (eq op byte-goto-if-nil-else-pop)
156: (eq op byte-goto-if-not-nil-else-pop)
157: (eq op byte-call)
158: (eq op byte-unbind))
159: ;; it's a number
160: offset) ;return it
161: ((or (eq op byte-constant)
162: (eq op byte-constant2))
163: ;; it's a constant
164: (setq tmp (aref constants offset))
165: ;; but is constant byte code?
166: (cond ((and (eq (car-safe tmp) 'lambda)
167: (assq 'byte-code tmp))
168: (princ "<compiled lambda>" stream)
169: (terpri stream)
170: (disassemble ;recurse on compiled lambda
171: tmp
172: stream
173: (+ indent disassemble-recursive-indent))
174: "")
175: ((eq (car-safe tmp) 'byte-code)
176: (princ "<byte code>" stream)
177: (terpri stream)
178: (disassemble-1 ;recurse on byte-code object
179: tmp
180: stream
181: (+ indent disassemble-recursive-indent))
182: "")
183: ((eq (car-safe (car-safe tmp)) 'byte-code)
184: (princ "(<byte code>...)" stream)
185: (terpri stream)
186: (mapcar ;recurse on list of byte-code objects
187: (function (lambda (obj)
188: (disassemble-1
189: obj
190: stream
191: (+ indent disassemble-recursive-indent))))
192: tmp)
193: "")
194: ((and (eq tmp 'byte-code)
195: (eq (aref bytes (+ ptr 4)) (+ byte-call 3)))
196: ;; this won't catch cases where args are pushed w/
197: ;; constant2.
198: (setq ptr (+ ptr 4))
199: "<compiled call to byte-code. compiled code compiled?>")
200: (t
201: ;; really just a constant
202: (let ((print-escape-newlines t))
203: (prin1-to-string tmp)))))
204: (t "<error in disassembler>"))
205: stream))
206: (terpri stream)))
207: nil)
208:
209:
210: (defun disassemble-offset ()
211: "Don't call this!"
212: ;; fetch and return the offset for the current opcode.
213: ;; return NIL if this opcode has no offset
214: ;; OP, PTR and BYTES are used and set dynamically
215: (let (tem)
216: (cond ((< op byte-nth)
217: (setq tem (logand op 7))
218: (setq op (logand op 248))
219: (cond ((eq tem 6)
220: (setq ptr (1+ ptr)) ;offset in next byte
221: (aref bytes ptr))
222: ((eq tem 7)
223: (setq ptr (1+ ptr)) ;offset in next 2 bytes
224: (+ (aref bytes ptr)
225: (progn (setq ptr (1+ ptr))
226: (lsh (aref bytes ptr) 8))))
227: (t tem))) ;offset was in opcode
228: ((>= op byte-constant)
229: (setq tem (- op byte-constant)) ;offset in opcode
230: (setq op byte-constant)
231: tem)
232: ((or (= op byte-constant2)
233: (and (>= op byte-goto)
234: (<= op byte-goto-if-not-nil-else-pop)))
235: (setq ptr (1+ ptr)) ;offset in next 2 bytes
236: (+ (aref bytes ptr)
237: (progn (setq ptr (1+ ptr))
238: (lsh (aref bytes ptr) 8))))
239: (t nil)))) ;no offset
240:
241:
242: (defun write-spaces (n &optional stream)
243: "Print N spaces to (optional) STREAM."
244: (or stream (setq stream standard-output))
245: (if (< n 0) (setq n 0))
246: (if (eq stream (current-buffer))
247: (insert-char ?\ n)
248: (while (> n 0)
249: (write-char ?\ stream)
250: (setq n (1- n)))))
251:
252: (defconst byte-code-vector
253: '[<not-an-opcode>
254: <not-an-opcode>
255: <not-an-opcode>
256: <not-an-opcode>
257: <not-an-opcode>
258: <not-an-opcode>
259: <not-an-opcode>
260: <not-an-opcode>
261: (varref . 1)
262: <not-an-opcode>
263: <not-an-opcode>
264: <not-an-opcode>
265: <not-an-opcode>
266: <not-an-opcode>
267: <not-an-opcode>
268: <not-an-opcode>
269: (varset . -1)
270: <not-an-opcode>
271: <not-an-opcode>
272: <not-an-opcode>
273: <not-an-opcode>
274: <not-an-opcode>
275: <not-an-opcode>
276: <not-an-opcode>
277: (varbind . 0);Pops a value, "pushes" a binding
278: <not-an-opcode>
279: <not-an-opcode>
280: <not-an-opcode>
281: <not-an-opcode>
282: <not-an-opcode>
283: <not-an-opcode>
284: <not-an-opcode>
285: (call . -); #'-, not -1!
286: <not-an-opcode>
287: <not-an-opcode>
288: <not-an-opcode>
289: <not-an-opcode>
290: <not-an-opcode>
291: <not-an-opcode>
292: <not-an-opcode>
293: (unbind . -);"pops" bindings
294: <not-an-opcode>
295: <not-an-opcode>
296: <not-an-opcode>
297: <not-an-opcode>
298: <not-an-opcode>
299: <not-an-opcode>
300: <not-an-opcode>
301: <not-an-opcode>
302: <not-an-opcode>
303: <not-an-opcode>
304: <not-an-opcode>
305: <not-an-opcode>
306: <not-an-opcode>
307: <not-an-opcode>
308: <not-an-opcode>
309: (nth . -1)
310: symbolp
311: consp
312: stringp
313: listp
314: (eq . -1)
315: (memq . -1)
316: not
317: car
318: cdr
319: (cons . -1)
320: list1
321: (list2 . -1)
322: (list3 . -2)
323: (list4 . -3)
324: length
325: (aref . -1)
326: (aset . -2)
327: symbol-value
328: symbol-function
329: (set . -1)
330: (fset . -1)
331: (get . -1)
332: (substring . -2)
333: (concat2 . -1)
334: (concat3 . -2)
335: (concat4 . -3)
336: sub1
337: add1
338: (eqlsign . -1) ;=
339: (gtr . -1) ;>
340: (lss . -1) ;<
341: (leq . -1) ;<=
342: (geq . -1) ;>=
343: (diff . -1) ;-
344: negate ;unary -
345: (plus . -1) ;+
346: (max . -1)
347: (min . -1)
348: <not-an-opcode>
349: (point . 1)
350: (mark\(obsolete\) . 1)
351: goto-char
352: insert
353: (point-max . 1)
354: (point-min . 1)
355: char-after
356: (following-char . 1)
357: (preceding-char . 1)
358: (current-column . 1)
359: (indent-to . 1)
360: (scan-buffer\(obsolete\) . -2)
361: (eolp . 1)
362: (eobp . 1)
363: (bolp . 1)
364: (bobp . 1)
365: (current-buffer . 1)
366: set-buffer
367: (read-char . 1)
368: set-mark\(obsolete\)
369: interactive-p
370: <not-an-opcode>
371: <not-an-opcode>
372: <not-an-opcode>
373: <not-an-opcode>
374: <not-an-opcode>
375: <not-an-opcode>
376: <not-an-opcode>
377: <not-an-opcode>
378: <not-an-opcode>
379: <not-an-opcode>
380: <not-an-opcode>
381: <not-an-opcode>
382: (constant2 . 1)
383: goto;>>>
384: goto-if-nil;>>
385: goto-if-not-nil;>>
386: (goto-if-nil-else-pop . -1)
387: (goto-if-not-nil-else-pop . -1)
388: return
389: (discard . -1)
390: (dup . 1)
391: (save-excursion . 1);Pushes a binding
392: (save-window-excursion . 1);Pushes a binding
393: (save-restriction . 1);Pushes a binding
394: (catch . -1);Takes one argument, returns a value
395: (unwind-protect . 1);Takes one argument, pushes a binding, returns a value
396: (condition-case . -2);Takes three arguments, returns a value
397: (temp-output-buffer-setup . -1)
398: temp-output-buffer-show
399: <not-an-opcode>
400: <not-an-opcode>
401: <not-an-opcode>
402: <not-an-opcode>
403: <not-an-opcode>
404: <not-an-opcode>
405: <not-an-opcode>
406: <not-an-opcode>
407: <not-an-opcode>
408: <not-an-opcode>
409: <not-an-opcode>
410: <not-an-opcode>
411: <not-an-opcode>
412: <not-an-opcode>
413: <not-an-opcode>
414: <not-an-opcode>
415: <not-an-opcode>
416: <not-an-opcode>
417: <not-an-opcode>
418: <not-an-opcode>
419: <not-an-opcode>
420: <not-an-opcode>
421: <not-an-opcode>
422: <not-an-opcode>
423: <not-an-opcode>
424: <not-an-opcode>
425: <not-an-opcode>
426: <not-an-opcode>
427: <not-an-opcode>
428: <not-an-opcode>
429: <not-an-opcode>
430: <not-an-opcode>
431: <not-an-opcode>
432: <not-an-opcode>
433: <not-an-opcode>
434: <not-an-opcode>
435: <not-an-opcode>
436: <not-an-opcode>
437: <not-an-opcode>
438: <not-an-opcode>
439: <not-an-opcode>
440: <not-an-opcode>
441: <not-an-opcode>
442: <not-an-opcode>
443: <not-an-opcode>
444: <not-an-opcode>
445: (constant . 1)
446: ])
447:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.