blob: fe5d24e88058f745b7df7ddab4c5efc23e7bb27e [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
Irit Katrielc5bfb882021-11-09 22:05:30 +0000431 extended_arg = 0
Serhiy Storchakab0f80b02016-05-24 09:15:14 +0300432 yield (i, op, arg)
Serhiy Storchaka02d9f5e2016-05-08 23:43:50 +0300433
Guido van Rossum217a5fa1990-12-26 15:40:07 +0000434def findlabels(code):
Tim Peters88869f92001-01-14 23:36:06 +0000435 """Detect all offsets in a byte code which are jump targets.
Guido van Rossum421c2241997-11-18 15:47:55 +0000436
Tim Peters88869f92001-01-14 23:36:06 +0000437 Return the list of offsets.
Guido van Rossum421c2241997-11-18 15:47:55 +0000438
Tim Peters88869f92001-01-14 23:36:06 +0000439 """
440 labels = []
Serhiy Storchaka02d9f5e2016-05-08 23:43:50 +0300441 for offset, op, arg in _unpack_opargs(code):
442 if arg is not None:
Tim Peters88869f92001-01-14 23:36:06 +0000443 if op in hasjrel:
Mark Shannonfcb55c02021-04-01 16:00:31 +0100444 label = offset + 2 + arg*2
Tim Peters88869f92001-01-14 23:36:06 +0000445 elif op in hasjabs:
Mark Shannonfcb55c02021-04-01 16:00:31 +0100446 label = arg*2
Serhiy Storchakab0f80b02016-05-24 09:15:14 +0300447 else:
448 continue
449 if label not in labels:
450 labels.append(label)
Tim Peters88869f92001-01-14 23:36:06 +0000451 return labels
Guido van Rossum217a5fa1990-12-26 15:40:07 +0000452
Armin Rigo9c8f7ea2003-10-28 12:17:25 +0000453def findlinestarts(code):
454 """Find the offsets in a byte code which are start of lines in the source.
455
Mark Shannon877df852020-11-12 09:43:29 +0000456 Generate pairs (offset, lineno)
Armin Rigo9c8f7ea2003-10-28 12:17:25 +0000457 """
Mark Shannon877df852020-11-12 09:43:29 +0000458 lastline = None
459 for start, end, line in code.co_lines():
460 if line is not None and line != lastline:
461 lastline = line
462 yield start, line
463 return
Armin Rigo9c8f7ea2003-10-28 12:17:25 +0000464
Guido van Rossum1fdae122000-02-04 17:47:55 +0000465
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000466class Bytecode:
467 """The bytecode operations of a piece of code
468
syncosmicfe2b56a2017-08-17 19:29:21 -0700469 Instantiate this with a function, method, other compiled object, string of
470 code, or a code object (as returned by compile()).
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000471
472 Iterating over this yields the bytecode operations as Instruction instances.
473 """
Nick Coghlan50c48b82013-11-23 00:57:00 +1000474 def __init__(self, x, *, first_line=None, current_offset=None):
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000475 self.codeobj = co = _get_code_object(x)
476 if first_line is None:
477 self.first_line = co.co_firstlineno
478 self._line_offset = 0
479 else:
480 self.first_line = first_line
481 self._line_offset = first_line - co.co_firstlineno
482 self._cell_names = co.co_cellvars + co.co_freevars
483 self._linestarts = dict(findlinestarts(co))
484 self._original_object = x
Nick Coghlan50c48b82013-11-23 00:57:00 +1000485 self.current_offset = current_offset
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000486
487 def __iter__(self):
488 co = self.codeobj
489 return _get_instructions_bytes(co.co_code, co.co_varnames, co.co_names,
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000490 co.co_consts, self._cell_names,
491 self._linestarts,
492 line_offset=self._line_offset)
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000493
494 def __repr__(self):
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000495 return "{}({!r})".format(self.__class__.__name__,
496 self._original_object)
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000497
Nick Coghlan50c48b82013-11-23 00:57:00 +1000498 @classmethod
499 def from_traceback(cls, tb):
500 """ Construct a Bytecode from the given traceback """
501 while tb.tb_next:
502 tb = tb.tb_next
503 return cls(tb.tb_frame.f_code, current_offset=tb.tb_lasti)
504
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000505 def info(self):
506 """Return formatted information about the code object."""
507 return _format_code_info(self.codeobj)
508
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000509 def dis(self):
510 """Return a formatted view of the bytecode operations."""
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000511 co = self.codeobj
Nick Coghlan50c48b82013-11-23 00:57:00 +1000512 if self.current_offset is not None:
513 offset = self.current_offset
514 else:
515 offset = -1
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000516 with io.StringIO() as output:
517 _disassemble_bytes(co.co_code, varnames=co.co_varnames,
518 names=co.co_names, constants=co.co_consts,
519 cells=self._cell_names,
520 linestarts=self._linestarts,
521 line_offset=self._line_offset,
Nick Coghlan50c48b82013-11-23 00:57:00 +1000522 file=output,
523 lasti=offset)
Nick Coghlan90b8e7d2013-11-06 22:08:36 +1000524 return output.getvalue()
Nick Coghlanb39fd0c2013-05-06 23:59:20 +1000525
526
Guido van Rossum1fdae122000-02-04 17:47:55 +0000527def _test():
Tim Peters88869f92001-01-14 23:36:06 +0000528 """Simple test program to disassemble a file."""
Nick Coghlan09566892013-08-25 00:48:17 +1000529 import argparse
530
531 parser = argparse.ArgumentParser()
Kongea4084b92020-08-08 11:03:09 +0800532 parser.add_argument('infile', type=argparse.FileType('rb'), nargs='?', default='-')
Nick Coghlan09566892013-08-25 00:48:17 +1000533 args = parser.parse_args()
534 with args.infile as infile:
535 source = infile.read()
536 code = compile(source, args.infile.name, "exec")
Tim Peters88869f92001-01-14 23:36:06 +0000537 dis(code)
Guido van Rossum1fdae122000-02-04 17:47:55 +0000538
539if __name__ == "__main__":
Tim Peters88869f92001-01-14 23:36:06 +0000540 _test()