Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | """Disassembler of Python byte code into mnemonics.""" |
2 | ||
3 | import sys | |
4 | import types | |
5 | ||
6 | from opcode import * | |
7 | from opcode import __all__ as _opcodes_all | |
8 | ||
9 | __all__ = ["dis","disassemble","distb","disco"] + _opcodes_all | |
10 | del _opcodes_all | |
11 | ||
12 | def dis(x=None): | |
13 | """Disassemble classes, methods, functions, or code. | |
14 | ||
15 | With no argument, disassemble the last traceback. | |
16 | ||
17 | """ | |
18 | if x is None: | |
19 | distb() | |
20 | return | |
21 | if type(x) is types.InstanceType: | |
22 | x = x.__class__ | |
23 | if hasattr(x, 'im_func'): | |
24 | x = x.im_func | |
25 | if hasattr(x, 'func_code'): | |
26 | x = x.func_code | |
27 | if hasattr(x, '__dict__'): | |
28 | items = x.__dict__.items() | |
29 | items.sort() | |
30 | for name, x1 in items: | |
31 | if type(x1) in (types.MethodType, | |
32 | types.FunctionType, | |
33 | types.CodeType, | |
34 | types.ClassType): | |
35 | print "Disassembly of %s:" % name | |
36 | try: | |
37 | dis(x1) | |
38 | except TypeError, msg: | |
39 | print "Sorry:", msg | |
40 | ||
41 | elif hasattr(x, 'co_code'): | |
42 | disassemble(x) | |
43 | elif isinstance(x, str): | |
44 | disassemble_string(x) | |
45 | else: | |
46 | raise TypeError, \ | |
47 | "don't know how to disassemble %s objects" % \ | |
48 | type(x).__name__ | |
49 | ||
50 | def distb(tb=None): | |
51 | """Disassemble a traceback (default: last traceback).""" | |
52 | if tb is None: | |
53 | try: | |
54 | tb = sys.last_traceback | |
55 | except AttributeError: | |
56 | raise RuntimeError, "no last traceback to disassemble" | |
57 | while tb.tb_next: tb = tb.tb_next | |
58 | disassemble(tb.tb_frame.f_code, tb.tb_lasti) | |
59 | ||
60 | def disassemble(co, lasti=-1): | |
61 | """Disassemble a code object.""" | |
62 | code = co.co_code | |
63 | labels = findlabels(code) | |
64 | linestarts = dict(findlinestarts(co)) | |
65 | n = len(code) | |
66 | i = 0 | |
67 | extended_arg = 0 | |
68 | free = None | |
69 | while i < n: | |
70 | c = code[i] | |
71 | op = ord(c) | |
72 | if i in linestarts: | |
73 | if i > 0: | |
74 | ||
75 | print "%3d" % linestarts[i], | |
76 | else: | |
77 | print ' ', | |
78 | ||
79 | if i == lasti: print '-->', | |
80 | else: print ' ', | |
81 | if i in labels: print '>>', | |
82 | else: print ' ', | |
83 | print repr(i).rjust(4), | |
84 | print opname[op].ljust(20), | |
85 | i = i+1 | |
86 | if op >= HAVE_ARGUMENT: | |
87 | oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg | |
88 | extended_arg = 0 | |
89 | i = i+2 | |
90 | if op == EXTENDED_ARG: | |
91 | extended_arg = oparg*65536L | |
92 | print repr(oparg).rjust(5), | |
93 | if op in hasconst: | |
94 | print '(' + repr(co.co_consts[oparg]) + ')', | |
95 | elif op in hasname: | |
96 | print '(' + co.co_names[oparg] + ')', | |
97 | elif op in hasjrel: | |
98 | print '(to ' + repr(i + oparg) + ')', | |
99 | elif op in haslocal: | |
100 | print '(' + co.co_varnames[oparg] + ')', | |
101 | elif op in hascompare: | |
102 | print '(' + cmp_op[oparg] + ')', | |
103 | elif op in hasfree: | |
104 | if free is None: | |
105 | free = co.co_cellvars + co.co_freevars | |
106 | print '(' + free[oparg] + ')', | |
107 | ||
108 | ||
109 | def disassemble_string(code, lasti=-1, varnames=None, names=None, | |
110 | constants=None): | |
111 | labels = findlabels(code) | |
112 | n = len(code) | |
113 | i = 0 | |
114 | while i < n: | |
115 | c = code[i] | |
116 | op = ord(c) | |
117 | if i == lasti: print '-->', | |
118 | else: print ' ', | |
119 | if i in labels: print '>>', | |
120 | else: print ' ', | |
121 | print repr(i).rjust(4), | |
122 | print opname[op].ljust(15), | |
123 | i = i+1 | |
124 | if op >= HAVE_ARGUMENT: | |
125 | oparg = ord(code[i]) + ord(code[i+1])*256 | |
126 | i = i+2 | |
127 | print repr(oparg).rjust(5), | |
128 | if op in hasconst: | |
129 | if constants: | |
130 | print '(' + repr(constants[oparg]) + ')', | |
131 | else: | |
132 | print '(%d)'%oparg, | |
133 | elif op in hasname: | |
134 | if names is not None: | |
135 | print '(' + names[oparg] + ')', | |
136 | else: | |
137 | print '(%d)'%oparg, | |
138 | elif op in hasjrel: | |
139 | print '(to ' + repr(i + oparg) + ')', | |
140 | elif op in haslocal: | |
141 | if varnames: | |
142 | print '(' + varnames[oparg] + ')', | |
143 | else: | |
144 | print '(%d)' % oparg, | |
145 | elif op in hascompare: | |
146 | print '(' + cmp_op[oparg] + ')', | |
147 | ||
148 | ||
149 | disco = disassemble # XXX For backwards compatibility | |
150 | ||
151 | def findlabels(code): | |
152 | """Detect all offsets in a byte code which are jump targets. | |
153 | ||
154 | Return the list of offsets. | |
155 | ||
156 | """ | |
157 | labels = [] | |
158 | n = len(code) | |
159 | i = 0 | |
160 | while i < n: | |
161 | c = code[i] | |
162 | op = ord(c) | |
163 | i = i+1 | |
164 | if op >= HAVE_ARGUMENT: | |
165 | oparg = ord(code[i]) + ord(code[i+1])*256 | |
166 | i = i+2 | |
167 | label = -1 | |
168 | if op in hasjrel: | |
169 | label = i+oparg | |
170 | elif op in hasjabs: | |
171 | label = oparg | |
172 | if label >= 0: | |
173 | if label not in labels: | |
174 | labels.append(label) | |
175 | return labels | |
176 | ||
177 | def findlinestarts(code): | |
178 | """Find the offsets in a byte code which are start of lines in the source. | |
179 | ||
180 | Generate pairs (offset, lineno) as described in Python/compile.c. | |
181 | ||
182 | """ | |
183 | byte_increments = [ord(c) for c in code.co_lnotab[0::2]] | |
184 | line_increments = [ord(c) for c in code.co_lnotab[1::2]] | |
185 | ||
186 | lastlineno = None | |
187 | lineno = code.co_firstlineno | |
188 | addr = 0 | |
189 | for byte_incr, line_incr in zip(byte_increments, line_increments): | |
190 | if byte_incr: | |
191 | if lineno != lastlineno: | |
192 | yield (addr, lineno) | |
193 | lastlineno = lineno | |
194 | addr += byte_incr | |
195 | lineno += line_incr | |
196 | if lineno != lastlineno: | |
197 | yield (addr, lineno) | |
198 | ||
199 | def _test(): | |
200 | """Simple test program to disassemble a file.""" | |
201 | if sys.argv[1:]: | |
202 | if sys.argv[2:]: | |
203 | sys.stderr.write("usage: python dis.py [-|file]\n") | |
204 | sys.exit(2) | |
205 | fn = sys.argv[1] | |
206 | if not fn or fn == "-": | |
207 | fn = None | |
208 | else: | |
209 | fn = None | |
210 | if fn is None: | |
211 | f = sys.stdin | |
212 | else: | |
213 | f = open(fn) | |
214 | source = f.read() | |
215 | if fn is not None: | |
216 | f.close() | |
217 | else: | |
218 | fn = "<stdin>" | |
219 | code = compile(source, fn, "exec") | |
220 | dis(code) | |
221 | ||
222 | if __name__ == "__main__": | |
223 | _test() |