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