blob: 3fee1ce27725cda243fd3dce26f83d8591d8b6e2 [file] [log] [blame]
Guido van Rossum421c2241997-11-18 15:47:55 +00001"""Disassembler of Python byte code into mnemonics."""
Guido van Rossum217a5fa1990-12-26 15:40:07 +00002
3import sys
Guido van Rossum18aef3c1997-03-14 04:15:43 +00004import types
Nick Coghlanb39fd0c2013-05-06 23:59:20 +10005import collections
Nick Coghlan90b8e7d2013-11-06 22:08:36 +10006import io
Guido van Rossum217a5fa1990-12-26 15:40:07 +00007
Skip Montanaro19c6ba32003-02-27 21:29:27 +00008from opcode import *
9from opcode import __all__ as _opcodes_all
10
Nick Coghlan7646f7e2010-09-10 12:24:24 +000011__all__ = ["code_info", "dis", "disassemble", "distb", "disco",
Nick Coghlanb39fd0c2013-05-06 23:59:20 +100012 "findlinestarts", "findlabels", "show_code",
13 "get_instructions", "Instruction", "Bytecode"] + _opcodes_all
Skip Montanaro19c6ba32003-02-27 21:29:27 +000014del _opcodes_all
Skip Montanaroe99d5ea2001-01-20 19:54:20 +000015
Serhiy Storchaka585c93d2016-04-23 09:23:52 +030016_have_code = (types.MethodType, types.FunctionType, types.CodeType,
17 classmethod, staticmethod, type)
Benjamin Peterson6ef9a842010-04-04 23:26:50 +000018
Serhiy Storchakadd102f72016-10-08 12:34:25 +030019FORMAT_VALUE = opmap['FORMAT_VALUE']
Serhiy Storchakae2732d32018-03-11 11:07:06 +020020FORMAT_VALUE_CONVERTERS = (
21 (None, ''),
22 (str, 'str'),
23 (repr, 'repr'),
24 (ascii, 'ascii'),
25)
26MAKE_FUNCTION = opmap['MAKE_FUNCTION']
27MAKE_FUNCTION_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure')
28
Serhiy Storchakadd102f72016-10-08 12:34:25 +030029
Nick Coghlan5c8b54e2010-07-03 07:36:51 +000030def _try_compile(source, name):
31 """Attempts to compile the given source, first as an expression and
32 then as a statement if the first approach fails.
33
34 Utility function to accept strings in functions that otherwise
35 expect code objects
36 """
Nick Coghlan5c8b54e2010-07-03 07:36:51 +000037 try:
38 c = compile(source, name, 'eval')
39 except SyntaxError:
40 c = compile(source, name, 'exec')
41 return c
42
Serhiy Storchaka1efbf922017-06-11 14:09:39 +030043def dis(x=None, *, file=None, depth=None):
syncosmicfe2b56a2017-08-17 19:29:21 -070044 """Disassemble classes, methods, functions, and other compiled objects.
Guido van Rossum421c2241997-11-18 15:47:55 +000045
Tim Peters88869f92001-01-14 23:36:06 +000046 With no argument, disassemble the last traceback.
Guido van Rossum421c2241997-11-18 15:47:55 +000047
syncosmicfe2b56a2017-08-17 19:29:21 -070048 Compiled objects currently include generator objects, async generator
49 objects, and coroutine objects, all of which store their code object
50 in a special attribute.
Tim Peters88869f92001-01-14 23:36:06 +000051 """
Raymond Hettinger0f4940c2002-06-01 00:57:55 +000052 if x is None:
Nick Coghlan90b8e7d2013-11-06 22:08:36 +100053 distb(file=file)
Tim Peters88869f92001-01-14 23:36:06 +000054 return
syncosmicfe2b56a2017-08-17 19:29:21 -070055 # Extract functions from methods.
56 if hasattr(x, '__func__'):
Christian Heimesff737952007-11-27 10:40:20 +000057 x = x.__func__
syncosmicfe2b56a2017-08-17 19:29:21 -070058 # Extract compiled code objects from...
59 if hasattr(x, '__code__'): # ...a function, or
Neal Norwitz221085d2007-02-25 20:55:47 +000060 x = x.__code__
syncosmicfe2b56a2017-08-17 19:29:21 -070061 elif hasattr(x, 'gi_code'): #...a generator object, or
Nick Coghlanefd5df92014-07-25 23:02:56 +100062 x = x.gi_code
syncosmicfe2b56a2017-08-17 19:29:21 -070063 elif hasattr(x, 'ag_code'): #...an asynchronous generator object, or
64 x = x.ag_code
65 elif hasattr(x, 'cr_code'): #...a coroutine.
66 x = x.cr_code
67 # Perform the disassembly.
Nick Coghlaneae2da12010-08-17 08:03:36 +000068 if hasattr(x, '__dict__'): # Class or module
Guido van Rossume7ba4952007-06-06 23:52:48 +000069 items = sorted(x.__dict__.items())
Tim Peters88869f92001-01-14 23:36:06 +000070 for name, x1 in items:
Benjamin Peterson6ef9a842010-04-04 23:26:50 +000071 if isinstance(x1, _have_code):
Nick Coghlanb39fd0c2013-05-06 23:59:20 +100072 print("Disassembly of %s:" % name, file=file)
Tim Peters88869f92001-01-14 23:36:06 +000073 try:
Serhiy Storchaka1efbf922017-06-11 14:09:39 +030074 dis(x1, file=file, depth=depth)
Guido van Rossumb940e112007-01-10 16:19:56 +000075 except TypeError as msg:
Nick Coghlanb39fd0c2013-05-06 23:59:20 +100076 print("Sorry:", msg, file=file)
77 print(file=file)
Nick Coghlaneae2da12010-08-17 08:03:36 +000078 elif hasattr(x, 'co_code'): # Code object
Serhiy Storchaka1efbf922017-06-11 14:09:39 +030079 _disassemble_recursive(x, file=file, depth=depth)
Nick Coghlaneae2da12010-08-17 08:03:36 +000080 elif isinstance(x, (bytes, bytearray)): # Raw bytecode
Nick Coghlanb39fd0c2013-05-06 23:59:20 +100081 _disassemble_bytes(x, file=file)
Nick Coghlaneae2da12010-08-17 08:03:36 +000082 elif isinstance(x, str): # Source code
Serhiy Storchaka1efbf922017-06-11 14:09:39 +030083 _disassemble_str(x, file=file, depth=depth)
Tim Peters88869f92001-01-14 23:36:06 +000084 else:
Guido van Rossume7ba4952007-06-06 23:52:48 +000085 raise TypeError("don't know how to disassemble %s objects" %
86 type(x).__name__)
Guido van Rossum217a5fa1990-12-26 15:40:07 +000087
Nick Coghlanb39fd0c2013-05-06 23:59:20 +100088def distb(tb=None, *, file=None):
Tim Peters88869f92001-01-14 23:36:06 +000089 """Disassemble a traceback (default: last traceback)."""
Raymond Hettinger0f4940c2002-06-01 00:57:55 +000090 if tb is None:
Tim Peters88869f92001-01-14 23:36:06 +000091 try:
92 tb = sys.last_traceback
93 except AttributeError:
Serhiy Storchaka5affd232017-04-05 09:37:24 +030094 raise RuntimeError("no last traceback to disassemble") from None
Tim Peters88869f92001-01-14 23:36:06 +000095 while tb.tb_next: tb = tb.tb_next
Nick Coghlanb39fd0c2013-05-06 23:59:20 +100096 disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file)
Guido van Rossum217a5fa1990-12-26 15:40:07 +000097
Nick Coghlan09c81232010-08-17 10:18:16 +000098# The inspect module interrogates this dictionary to build its
99# list of CO_* constants. It is also used by pretty_flags to
100# turn the co_flags field into a human readable list.
101COMPILER_FLAG_NAMES = {
Guido van Rossum3e1b85e2007-05-30 02:07:00 +0000102 1: "OPTIMIZED",
103 2: "NEWLOCALS",
104 4: "VARARGS",
105 8: "VARKEYWORDS",
106 16: "NESTED",
107 32: "GENERATOR",
108 64: "NOFREE",
Yury Selivanov75445082015-05-11 22:57:16 -0400109 128: "COROUTINE",
110 256: "ITERABLE_COROUTINE",
Yury Selivanoveb636452016-09-08 22:01:51 -0700111 512: "ASYNC_GENERATOR",
Guido van Rossum3e1b85e2007-05-30 02:07:00 +0000112}
113
114def pretty_flags(flags):
115 """Return pretty representation of code flags."""
116 names = []
117 for i in range(32):
118 flag = 1<<i
119 if flags & flag:
Nick Coghlan09c81232010-08-17 10:18:16 +0000120 names.append(COMPILER_FLAG_NAMES.get(flag, hex(flag)))
Guido van Rossum3e1b85e2007-05-30 02:07:00 +0000121 flags ^= flag
122 if not flags:
123 break
124 else:
125 names.append(hex(flags))
126 return ", ".join(names)
127
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000128def _get_code_object(x):
syncosmicfe2b56a2017-08-17 19:29:21 -0700129 """Helper to handle methods, compiled or raw code objects, and strings."""
130 # Extract functions from methods.
131 if hasattr(x, '__func__'):
Nick Coghlaneae2da12010-08-17 08:03:36 +0000132 x = x.__func__
syncosmicfe2b56a2017-08-17 19:29:21 -0700133 # Extract compiled code objects from...
134 if hasattr(x, '__code__'): # ...a function, or
Nick Coghlaneae2da12010-08-17 08:03:36 +0000135 x = x.__code__
syncosmicfe2b56a2017-08-17 19:29:21 -0700136 elif hasattr(x, 'gi_code'): #...a generator object, or
Nick Coghlanefd5df92014-07-25 23:02:56 +1000137 x = x.gi_code
syncosmicfe2b56a2017-08-17 19:29:21 -0700138 elif hasattr(x, 'ag_code'): #...an asynchronous generator object, or
139 x = x.ag_code
140 elif hasattr(x, 'cr_code'): #...a coroutine.
141 x = x.cr_code
142 # Handle source code.
143 if isinstance(x, str):
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000144 x = _try_compile(x, "<disassembly>")
syncosmicfe2b56a2017-08-17 19:29:21 -0700145 # By now, if we don't have a code object, we can't disassemble x.
146 if hasattr(x, 'co_code'):
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000147 return x
148 raise TypeError("don't know how to disassemble %s objects" %
149 type(x).__name__)
150
151def code_info(x):
152 """Formatted details of methods, functions, or code."""
153 return _format_code_info(_get_code_object(x))
Nick Coghlaneae2da12010-08-17 08:03:36 +0000154
155def _format_code_info(co):
156 lines = []
157 lines.append("Name: %s" % co.co_name)
158 lines.append("Filename: %s" % co.co_filename)
159 lines.append("Argument count: %s" % co.co_argcount)
Pablo Galindo8c77b8c2019-04-29 13:36:57 +0100160 lines.append("Positional-only arguments: %s" % co.co_posonlyargcount)
Nick Coghlaneae2da12010-08-17 08:03:36 +0000161 lines.append("Kw-only arguments: %s" % co.co_kwonlyargcount)
162 lines.append("Number of locals: %s" % co.co_nlocals)
163 lines.append("Stack size: %s" % co.co_stacksize)
164 lines.append("Flags: %s" % pretty_flags(co.co_flags))
165 if co.co_consts:
166 lines.append("Constants:")
167 for i_c in enumerate(co.co_consts):
168 lines.append("%4d: %r" % i_c)
169 if co.co_names:
170 lines.append("Names:")
171 for i_n in enumerate(co.co_names):
172 lines.append("%4d: %s" % i_n)
173 if co.co_varnames:
174 lines.append("Variable names:")
175 for i_n in enumerate(co.co_varnames):
176 lines.append("%4d: %s" % i_n)
177 if co.co_freevars:
178 lines.append("Free variables:")
179 for i_n in enumerate(co.co_freevars):
180 lines.append("%4d: %s" % i_n)
181 if co.co_cellvars:
182 lines.append("Cell variables:")
183 for i_n in enumerate(co.co_cellvars):
184 lines.append("%4d: %s" % i_n)
185 return "\n".join(lines)
186
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000187def show_code(co, *, file=None):
Ezio Melotti6e6c6ac2013-08-23 22:41:39 +0300188 """Print details of methods, functions, or code to *file*.
189
190 If *file* is not provided, the output is printed on stdout.
191 """
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000192 print(code_info(co), file=file)
Guido van Rossum3e1b85e2007-05-30 02:07:00 +0000193
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000194_Instruction = collections.namedtuple("_Instruction",
195 "opname opcode arg argval argrepr offset starts_line is_jump_target")
196
Raymond Hettinger5b798ab2015-08-17 22:04:45 -0700197_Instruction.opname.__doc__ = "Human readable name for operation"
198_Instruction.opcode.__doc__ = "Numeric code for operation"
199_Instruction.arg.__doc__ = "Numeric argument to operation (if any), otherwise None"
200_Instruction.argval.__doc__ = "Resolved arg value (if known), otherwise same as arg"
201_Instruction.argrepr.__doc__ = "Human readable description of operation argument"
202_Instruction.offset.__doc__ = "Start index of operation within bytecode sequence"
203_Instruction.starts_line.__doc__ = "Line started by this opcode (if any), otherwise None"
204_Instruction.is_jump_target.__doc__ = "True if other code jumps to here, otherwise False"
205
Serhiy Storchakad90045f2017-04-19 20:36:31 +0300206_OPNAME_WIDTH = 20
207_OPARG_WIDTH = 5
208
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000209class Instruction(_Instruction):
210 """Details for a bytecode operation
211
212 Defined fields:
213 opname - human readable name for operation
214 opcode - numeric code for operation
215 arg - numeric argument to operation (if any), otherwise None
216 argval - resolved arg value (if known), otherwise same as arg
217 argrepr - human readable description of operation argument
218 offset - start index of operation within bytecode sequence
219 starts_line - line started by this opcode (if any), otherwise None
220 is_jump_target - True if other code jumps to here, otherwise False
221 """
222
Serhiy Storchakad90045f2017-04-19 20:36:31 +0300223 def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=4):
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000224 """Format instruction details for inclusion in disassembly output
225
226 *lineno_width* sets the width of the line number field (0 omits it)
227 *mark_as_current* inserts a '-->' marker arrow as part of the line
Serhiy Storchakad90045f2017-04-19 20:36:31 +0300228 *offset_width* sets the width of the instruction offset field
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000229 """
230 fields = []
231 # Column: Source code line number
232 if lineno_width:
233 if self.starts_line is not None:
234 lineno_fmt = "%%%dd" % lineno_width
235 fields.append(lineno_fmt % self.starts_line)
236 else:
237 fields.append(' ' * lineno_width)
238 # Column: Current instruction indicator
239 if mark_as_current:
240 fields.append('-->')
241 else:
242 fields.append(' ')
243 # Column: Jump target marker
244 if self.is_jump_target:
245 fields.append('>>')
246 else:
247 fields.append(' ')
248 # Column: Instruction offset from start of code sequence
Serhiy Storchakad90045f2017-04-19 20:36:31 +0300249 fields.append(repr(self.offset).rjust(offset_width))
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000250 # Column: Opcode name
Serhiy Storchakad90045f2017-04-19 20:36:31 +0300251 fields.append(self.opname.ljust(_OPNAME_WIDTH))
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000252 # Column: Opcode argument
253 if self.arg is not None:
Serhiy Storchakad90045f2017-04-19 20:36:31 +0300254 fields.append(repr(self.arg).rjust(_OPARG_WIDTH))
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000255 # Column: Opcode argument details
256 if self.argrepr:
257 fields.append('(' + self.argrepr + ')')
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000258 return ' '.join(fields).rstrip()
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000259
260
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000261def get_instructions(x, *, first_line=None):
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000262 """Iterator for the opcodes in methods, functions or code
263
264 Generates a series of Instruction named tuples giving the details of
265 each operations in the supplied code.
266
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000267 If *first_line* is not None, it indicates the line number that should
268 be reported for the first source line in the disassembled code.
269 Otherwise, the source line information (if any) is taken directly from
270 the disassembled code object.
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000271 """
272 co = _get_code_object(x)
273 cell_names = co.co_cellvars + co.co_freevars
Armin Rigo9c8f7ea2003-10-28 12:17:25 +0000274 linestarts = dict(findlinestarts(co))
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000275 if first_line is not None:
276 line_offset = first_line - co.co_firstlineno
277 else:
278 line_offset = 0
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000279 return _get_instructions_bytes(co.co_code, co.co_varnames, co.co_names,
280 co.co_consts, cell_names, linestarts,
281 line_offset)
282
283def _get_const_info(const_index, const_list):
284 """Helper to get optional details about const references
285
286 Returns the dereferenced constant and its repr if the constant
287 list is defined.
288 Otherwise returns the constant index and its repr().
289 """
290 argval = const_index
291 if const_list is not None:
292 argval = const_list[const_index]
293 return argval, repr(argval)
294
295def _get_name_info(name_index, name_list):
296 """Helper to get optional details about named references
297
298 Returns the dereferenced name as both value and repr if the name
299 list is defined.
300 Otherwise returns the name index and its repr().
301 """
302 argval = name_index
303 if name_list is not None:
304 argval = name_list[name_index]
305 argrepr = argval
306 else:
307 argrepr = repr(argval)
308 return argval, argrepr
309
310
311def _get_instructions_bytes(code, varnames=None, names=None, constants=None,
312 cells=None, linestarts=None, line_offset=0):
313 """Iterate over the instructions in a bytecode string.
314
315 Generates a sequence of Instruction namedtuples giving the details of each
316 opcode. Additional information about the code's runtime environment
317 (e.g. variable names, constants) can be specified using optional
318 arguments.
319
320 """
321 labels = findlabels(code)
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000322 starts_line = None
Serhiy Storchaka02d9f5e2016-05-08 23:43:50 +0300323 for offset, op, arg in _unpack_opargs(code):
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000324 if linestarts is not None:
Serhiy Storchaka02d9f5e2016-05-08 23:43:50 +0300325 starts_line = linestarts.get(offset, None)
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000326 if starts_line is not None:
327 starts_line += line_offset
Serhiy Storchaka02d9f5e2016-05-08 23:43:50 +0300328 is_jump_target = offset in labels
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000329 argval = None
330 argrepr = ''
Serhiy Storchaka02d9f5e2016-05-08 23:43:50 +0300331 if arg is not None:
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000332 # Set argval to the dereferenced value of the argument when
Serhiy Storchakab0f80b02016-05-24 09:15:14 +0300333 # available, and argrepr to the string representation of argval.
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000334 # _disassemble_bytes needs the string repr of the
335 # raw name index for LOAD_GLOBAL, LOAD_CONST, etc.
336 argval = arg
Tim Peters88869f92001-01-14 23:36:06 +0000337 if op in hasconst:
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000338 argval, argrepr = _get_const_info(arg, constants)
Tim Peters88869f92001-01-14 23:36:06 +0000339 elif op in hasname:
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000340 argval, argrepr = _get_name_info(arg, names)
Mark Shannonfcb55c02021-04-01 16:00:31 +0100341 elif op in hasjabs:
342 argval = arg*2
343 argrepr = "to " + repr(argval)
Tim Peters88869f92001-01-14 23:36:06 +0000344 elif op in hasjrel:
Mark Shannonfcb55c02021-04-01 16:00:31 +0100345 argval = offset + 2 + arg*2
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000346 argrepr = "to " + repr(argval)
Tim Peters88869f92001-01-14 23:36:06 +0000347 elif op in haslocal:
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000348 argval, argrepr = _get_name_info(arg, varnames)
Tim Peters88869f92001-01-14 23:36:06 +0000349 elif op in hascompare:
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000350 argval = cmp_op[arg]
351 argrepr = argval
Jeremy Hyltona39414b2001-01-25 20:08:47 +0000352 elif op in hasfree:
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000353 argval, argrepr = _get_name_info(arg, cells)
Serhiy Storchakadd102f72016-10-08 12:34:25 +0300354 elif op == FORMAT_VALUE:
Serhiy Storchakae2732d32018-03-11 11:07:06 +0200355 argval, argrepr = FORMAT_VALUE_CONVERTERS[arg & 0x3]
356 argval = (argval, bool(arg & 0x4))
Serhiy Storchakadd102f72016-10-08 12:34:25 +0300357 if argval[1]:
358 if argrepr:
359 argrepr += ', '
360 argrepr += 'with format'
Serhiy Storchakae2732d32018-03-11 11:07:06 +0200361 elif op == MAKE_FUNCTION:
362 argrepr = ', '.join(s for i, s in enumerate(MAKE_FUNCTION_FLAGS)
363 if arg & (1<<i))
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000364 yield Instruction(opname[op], op,
365 arg, argval, argrepr,
366 offset, starts_line, is_jump_target)
367
368def disassemble(co, lasti=-1, *, file=None):
369 """Disassemble a code object."""
370 cell_names = co.co_cellvars + co.co_freevars
371 linestarts = dict(findlinestarts(co))
372 _disassemble_bytes(co.co_code, lasti, co.co_varnames, co.co_names,
373 co.co_consts, cell_names, linestarts, file=file)
Guido van Rossum217a5fa1990-12-26 15:40:07 +0000374
Serhiy Storchaka1efbf922017-06-11 14:09:39 +0300375def _disassemble_recursive(co, *, file=None, depth=None):
376 disassemble(co, file=file)
377 if depth is None or depth > 0:
378 if depth is not None:
379 depth = depth - 1
380 for x in co.co_consts:
381 if hasattr(x, 'co_code'):
382 print(file=file)
383 print("Disassembly of %r:" % (x,), file=file)
384 _disassemble_recursive(x, file=file, depth=depth)
385
Nick Coghlan5c8b54e2010-07-03 07:36:51 +0000386def _disassemble_bytes(code, lasti=-1, varnames=None, names=None,
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000387 constants=None, cells=None, linestarts=None,
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000388 *, file=None, line_offset=0):
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000389 # Omit the line number column entirely if we have no line number info
Yurii Karabasf24b8102020-12-04 17:20:53 +0200390 show_lineno = bool(linestarts)
Serhiy Storchakad90045f2017-04-19 20:36:31 +0300391 if show_lineno:
392 maxlineno = max(linestarts.values()) + line_offset
393 if maxlineno >= 1000:
394 lineno_width = len(str(maxlineno))
395 else:
396 lineno_width = 3
397 else:
398 lineno_width = 0
399 maxoffset = len(code) - 2
400 if maxoffset >= 10000:
401 offset_width = len(str(maxoffset))
402 else:
403 offset_width = 4
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000404 for instr in _get_instructions_bytes(code, varnames, names,
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000405 constants, cells, linestarts,
406 line_offset=line_offset):
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000407 new_source_line = (show_lineno and
408 instr.starts_line is not None and
409 instr.offset > 0)
410 if new_source_line:
411 print(file=file)
412 is_current_instr = instr.offset == lasti
Serhiy Storchakad90045f2017-04-19 20:36:31 +0300413 print(instr._disassemble(lineno_width, is_current_instr, offset_width),
414 file=file)
Skip Montanaro19c6ba32003-02-27 21:29:27 +0000415
Serhiy Storchaka1efbf922017-06-11 14:09:39 +0300416def _disassemble_str(source, **kwargs):
Nick Coghlan5c8b54e2010-07-03 07:36:51 +0000417 """Compile the source string, then disassemble the code object."""
Serhiy Storchaka1efbf922017-06-11 14:09:39 +0300418 _disassemble_recursive(_try_compile(source, '<dis>'), **kwargs)
Nick Coghlan5c8b54e2010-07-03 07:36:51 +0000419
Tim Peters88869f92001-01-14 23:36:06 +0000420disco = disassemble # XXX For backwards compatibility
Guido van Rossumbd307951997-01-17 20:05:04 +0000421
Serhiy Storchaka02d9f5e2016-05-08 23:43:50 +0300422def _unpack_opargs(code):
Serhiy Storchaka02d9f5e2016-05-08 23:43:50 +0300423 extended_arg = 0
Serhiy Storchakab0f80b02016-05-24 09:15:14 +0300424 for i in range(0, len(code), 2):
Serhiy Storchaka02d9f5e2016-05-08 23:43:50 +0300425 op = code[i]
Serhiy Storchaka02d9f5e2016-05-08 23:43:50 +0300426 if op >= HAVE_ARGUMENT:
Serhiy Storchakab0f80b02016-05-24 09:15:14 +0300427 arg = code[i+1] | extended_arg
428 extended_arg = (arg << 8) if op == EXTENDED_ARG else 0
429 else:
430 arg = None
431 yield (i, op, arg)
Serhiy Storchaka02d9f5e2016-05-08 23:43:50 +0300432
Guido van Rossum217a5fa1990-12-26 15:40:07 +0000433def findlabels(code):
Tim Peters88869f92001-01-14 23:36:06 +0000434 """Detect all offsets in a byte code which are jump targets.
Guido van Rossum421c2241997-11-18 15:47:55 +0000435
Tim Peters88869f92001-01-14 23:36:06 +0000436 Return the list of offsets.
Guido van Rossum421c2241997-11-18 15:47:55 +0000437
Tim Peters88869f92001-01-14 23:36:06 +0000438 """
439 labels = []
Serhiy Storchaka02d9f5e2016-05-08 23:43:50 +0300440 for offset, op, arg in _unpack_opargs(code):
441 if arg is not None:
Tim Peters88869f92001-01-14 23:36:06 +0000442 if op in hasjrel:
Mark Shannonfcb55c02021-04-01 16:00:31 +0100443 label = offset + 2 + arg*2
Tim Peters88869f92001-01-14 23:36:06 +0000444 elif op in hasjabs:
Mark Shannonfcb55c02021-04-01 16:00:31 +0100445 label = arg*2
Serhiy Storchakab0f80b02016-05-24 09:15:14 +0300446 else:
447 continue
448 if label not in labels:
449 labels.append(label)
Tim Peters88869f92001-01-14 23:36:06 +0000450 return labels
Guido van Rossum217a5fa1990-12-26 15:40:07 +0000451
Armin Rigo9c8f7ea2003-10-28 12:17:25 +0000452def findlinestarts(code):
453 """Find the offsets in a byte code which are start of lines in the source.
454
Mark Shannon877df852020-11-12 09:43:29 +0000455 Generate pairs (offset, lineno)
Armin Rigo9c8f7ea2003-10-28 12:17:25 +0000456 """
Mark Shannon877df852020-11-12 09:43:29 +0000457 lastline = None
458 for start, end, line in code.co_lines():
459 if line is not None and line != lastline:
460 lastline = line
461 yield start, line
462 return
Armin Rigo9c8f7ea2003-10-28 12:17:25 +0000463
Guido van Rossum1fdae122000-02-04 17:47:55 +0000464
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000465class Bytecode:
466 """The bytecode operations of a piece of code
467
syncosmicfe2b56a2017-08-17 19:29:21 -0700468 Instantiate this with a function, method, other compiled object, string of
469 code, or a code object (as returned by compile()).
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000470
471 Iterating over this yields the bytecode operations as Instruction instances.
472 """
Nick Coghlan50c48b82013-11-23 00:57:00 +1000473 def __init__(self, x, *, first_line=None, current_offset=None):
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000474 self.codeobj = co = _get_code_object(x)
475 if first_line is None:
476 self.first_line = co.co_firstlineno
477 self._line_offset = 0
478 else:
479 self.first_line = first_line
480 self._line_offset = first_line - co.co_firstlineno
481 self._cell_names = co.co_cellvars + co.co_freevars
482 self._linestarts = dict(findlinestarts(co))
483 self._original_object = x
Nick Coghlan50c48b82013-11-23 00:57:00 +1000484 self.current_offset = current_offset
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000485
486 def __iter__(self):
487 co = self.codeobj
488 return _get_instructions_bytes(co.co_code, co.co_varnames, co.co_names,
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000489 co.co_consts, self._cell_names,
490 self._linestarts,
491 line_offset=self._line_offset)
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000492
493 def __repr__(self):
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000494 return "{}({!r})".format(self.__class__.__name__,
495 self._original_object)
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000496
Nick Coghlan50c48b82013-11-23 00:57:00 +1000497 @classmethod
498 def from_traceback(cls, tb):
499 """ Construct a Bytecode from the given traceback """
500 while tb.tb_next:
501 tb = tb.tb_next
502 return cls(tb.tb_frame.f_code, current_offset=tb.tb_lasti)
503
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000504 def info(self):
505 """Return formatted information about the code object."""
506 return _format_code_info(self.codeobj)
507
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000508 def dis(self):
509 """Return a formatted view of the bytecode operations."""
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000510 co = self.codeobj
Nick Coghlan50c48b82013-11-23 00:57:00 +1000511 if self.current_offset is not None:
512 offset = self.current_offset
513 else:
514 offset = -1
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000515 with io.StringIO() as output:
516 _disassemble_bytes(co.co_code, varnames=co.co_varnames,
517 names=co.co_names, constants=co.co_consts,
518 cells=self._cell_names,
519 linestarts=self._linestarts,
520 line_offset=self._line_offset,
Nick Coghlan50c48b82013-11-23 00:57:00 +1000521 file=output,
522 lasti=offset)
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000523 return output.getvalue()
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000524
525
Guido van Rossum1fdae122000-02-04 17:47:55 +0000526def _test():
Tim Peters88869f92001-01-14 23:36:06 +0000527 """Simple test program to disassemble a file."""
Nick Coghlan09566892013-08-25 00:48:17 +1000528 import argparse
529
530 parser = argparse.ArgumentParser()
Kongea4084b92020-08-08 11:03:09 +0800531 parser.add_argument('infile', type=argparse.FileType('rb'), nargs='?', default='-')
Nick Coghlan09566892013-08-25 00:48:17 +1000532 args = parser.parse_args()
533 with args.infile as infile:
534 source = infile.read()
535 code = compile(source, args.infile.name, "exec")
Tim Peters88869f92001-01-14 23:36:06 +0000536 dis(code)
Guido van Rossum1fdae122000-02-04 17:47:55 +0000537
538if __name__ == "__main__":
Tim Peters88869f92001-01-14 23:36:06 +0000539 _test()