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