ยปCore Development>Code coverage>Lib/dis.py

Python code coverage for Lib/dis.py

#countcontent
1n/a"""Disassembler of Python byte code into mnemonics."""
2n/a
3n/aimport sys
4n/aimport types
5n/aimport collections
6n/aimport io
7n/a
8n/afrom opcode import *
9n/afrom opcode import __all__ as _opcodes_all
10n/a
11n/a__all__ = ["code_info", "dis", "disassemble", "distb", "disco",
12n/a "findlinestarts", "findlabels", "show_code",
13n/a "get_instructions", "Instruction", "Bytecode"] + _opcodes_all
14n/adel _opcodes_all
15n/a
16n/a_have_code = (types.MethodType, types.FunctionType, types.CodeType,
17n/a classmethod, staticmethod, type)
18n/a
19n/aFORMAT_VALUE = opmap['FORMAT_VALUE']
20n/a
21n/adef _try_compile(source, name):
22n/a """Attempts to compile the given source, first as an expression and
23n/a then as a statement if the first approach fails.
24n/a
25n/a Utility function to accept strings in functions that otherwise
26n/a expect code objects
27n/a """
28n/a try:
29n/a c = compile(source, name, 'eval')
30n/a except SyntaxError:
31n/a c = compile(source, name, 'exec')
32n/a return c
33n/a
34n/adef dis(x=None, *, file=None):
35n/a """Disassemble classes, methods, functions, generators, or code.
36n/a
37n/a With no argument, disassemble the last traceback.
38n/a
39n/a """
40n/a if x is None:
41n/a distb(file=file)
42n/a return
43n/a if hasattr(x, '__func__'): # Method
44n/a x = x.__func__
45n/a if hasattr(x, '__code__'): # Function
46n/a x = x.__code__
47n/a if hasattr(x, 'gi_code'): # Generator
48n/a x = x.gi_code
49n/a if hasattr(x, '__dict__'): # Class or module
50n/a items = sorted(x.__dict__.items())
51n/a for name, x1 in items:
52n/a if isinstance(x1, _have_code):
53n/a print("Disassembly of %s:" % name, file=file)
54n/a try:
55n/a dis(x1, file=file)
56n/a except TypeError as msg:
57n/a print("Sorry:", msg, file=file)
58n/a print(file=file)
59n/a elif hasattr(x, 'co_code'): # Code object
60n/a disassemble(x, file=file)
61n/a elif isinstance(x, (bytes, bytearray)): # Raw bytecode
62n/a _disassemble_bytes(x, file=file)
63n/a elif isinstance(x, str): # Source code
64n/a _disassemble_str(x, file=file)
65n/a else:
66n/a raise TypeError("don't know how to disassemble %s objects" %
67n/a type(x).__name__)
68n/a
69n/adef distb(tb=None, *, file=None):
70n/a """Disassemble a traceback (default: last traceback)."""
71n/a if tb is None:
72n/a try:
73n/a tb = sys.last_traceback
74n/a except AttributeError:
75n/a raise RuntimeError("no last traceback to disassemble")
76n/a while tb.tb_next: tb = tb.tb_next
77n/a disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file)
78n/a
79n/a# The inspect module interrogates this dictionary to build its
80n/a# list of CO_* constants. It is also used by pretty_flags to
81n/a# turn the co_flags field into a human readable list.
82n/aCOMPILER_FLAG_NAMES = {
83n/a 1: "OPTIMIZED",
84n/a 2: "NEWLOCALS",
85n/a 4: "VARARGS",
86n/a 8: "VARKEYWORDS",
87n/a 16: "NESTED",
88n/a 32: "GENERATOR",
89n/a 64: "NOFREE",
90n/a 128: "COROUTINE",
91n/a 256: "ITERABLE_COROUTINE",
92n/a 512: "ASYNC_GENERATOR",
93n/a}
94n/a
95n/adef pretty_flags(flags):
96n/a """Return pretty representation of code flags."""
97n/a names = []
98n/a for i in range(32):
99n/a flag = 1<<i
100n/a if flags & flag:
101n/a names.append(COMPILER_FLAG_NAMES.get(flag, hex(flag)))
102n/a flags ^= flag
103n/a if not flags:
104n/a break
105n/a else:
106n/a names.append(hex(flags))
107n/a return ", ".join(names)
108n/a
109n/adef _get_code_object(x):
110n/a """Helper to handle methods, functions, generators, strings and raw code objects"""
111n/a if hasattr(x, '__func__'): # Method
112n/a x = x.__func__
113n/a if hasattr(x, '__code__'): # Function
114n/a x = x.__code__
115n/a if hasattr(x, 'gi_code'): # Generator
116n/a x = x.gi_code
117n/a if isinstance(x, str): # Source code
118n/a x = _try_compile(x, "<disassembly>")
119n/a if hasattr(x, 'co_code'): # Code object
120n/a return x
121n/a raise TypeError("don't know how to disassemble %s objects" %
122n/a type(x).__name__)
123n/a
124n/adef code_info(x):
125n/a """Formatted details of methods, functions, or code."""
126n/a return _format_code_info(_get_code_object(x))
127n/a
128n/adef _format_code_info(co):
129n/a lines = []
130n/a lines.append("Name: %s" % co.co_name)
131n/a lines.append("Filename: %s" % co.co_filename)
132n/a lines.append("Argument count: %s" % co.co_argcount)
133n/a lines.append("Kw-only arguments: %s" % co.co_kwonlyargcount)
134n/a lines.append("Number of locals: %s" % co.co_nlocals)
135n/a lines.append("Stack size: %s" % co.co_stacksize)
136n/a lines.append("Flags: %s" % pretty_flags(co.co_flags))
137n/a if co.co_consts:
138n/a lines.append("Constants:")
139n/a for i_c in enumerate(co.co_consts):
140n/a lines.append("%4d: %r" % i_c)
141n/a if co.co_names:
142n/a lines.append("Names:")
143n/a for i_n in enumerate(co.co_names):
144n/a lines.append("%4d: %s" % i_n)
145n/a if co.co_varnames:
146n/a lines.append("Variable names:")
147n/a for i_n in enumerate(co.co_varnames):
148n/a lines.append("%4d: %s" % i_n)
149n/a if co.co_freevars:
150n/a lines.append("Free variables:")
151n/a for i_n in enumerate(co.co_freevars):
152n/a lines.append("%4d: %s" % i_n)
153n/a if co.co_cellvars:
154n/a lines.append("Cell variables:")
155n/a for i_n in enumerate(co.co_cellvars):
156n/a lines.append("%4d: %s" % i_n)
157n/a return "\n".join(lines)
158n/a
159n/adef show_code(co, *, file=None):
160n/a """Print details of methods, functions, or code to *file*.
161n/a
162n/a If *file* is not provided, the output is printed on stdout.
163n/a """
164n/a print(code_info(co), file=file)
165n/a
166n/a_Instruction = collections.namedtuple("_Instruction",
167n/a "opname opcode arg argval argrepr offset starts_line is_jump_target")
168n/a
169n/a_Instruction.opname.__doc__ = "Human readable name for operation"
170n/a_Instruction.opcode.__doc__ = "Numeric code for operation"
171n/a_Instruction.arg.__doc__ = "Numeric argument to operation (if any), otherwise None"
172n/a_Instruction.argval.__doc__ = "Resolved arg value (if known), otherwise same as arg"
173n/a_Instruction.argrepr.__doc__ = "Human readable description of operation argument"
174n/a_Instruction.offset.__doc__ = "Start index of operation within bytecode sequence"
175n/a_Instruction.starts_line.__doc__ = "Line started by this opcode (if any), otherwise None"
176n/a_Instruction.is_jump_target.__doc__ = "True if other code jumps to here, otherwise False"
177n/a
178n/aclass Instruction(_Instruction):
179n/a """Details for a bytecode operation
180n/a
181n/a Defined fields:
182n/a opname - human readable name for operation
183n/a opcode - numeric code for operation
184n/a arg - numeric argument to operation (if any), otherwise None
185n/a argval - resolved arg value (if known), otherwise same as arg
186n/a argrepr - human readable description of operation argument
187n/a offset - start index of operation within bytecode sequence
188n/a starts_line - line started by this opcode (if any), otherwise None
189n/a is_jump_target - True if other code jumps to here, otherwise False
190n/a """
191n/a
192n/a def _disassemble(self, lineno_width=3, mark_as_current=False):
193n/a """Format instruction details for inclusion in disassembly output
194n/a
195n/a *lineno_width* sets the width of the line number field (0 omits it)
196n/a *mark_as_current* inserts a '-->' marker arrow as part of the line
197n/a """
198n/a fields = []
199n/a # Column: Source code line number
200n/a if lineno_width:
201n/a if self.starts_line is not None:
202n/a lineno_fmt = "%%%dd" % lineno_width
203n/a fields.append(lineno_fmt % self.starts_line)
204n/a else:
205n/a fields.append(' ' * lineno_width)
206n/a # Column: Current instruction indicator
207n/a if mark_as_current:
208n/a fields.append('-->')
209n/a else:
210n/a fields.append(' ')
211n/a # Column: Jump target marker
212n/a if self.is_jump_target:
213n/a fields.append('>>')
214n/a else:
215n/a fields.append(' ')
216n/a # Column: Instruction offset from start of code sequence
217n/a fields.append(repr(self.offset).rjust(4))
218n/a # Column: Opcode name
219n/a fields.append(self.opname.ljust(20))
220n/a # Column: Opcode argument
221n/a if self.arg is not None:
222n/a fields.append(repr(self.arg).rjust(5))
223n/a # Column: Opcode argument details
224n/a if self.argrepr:
225n/a fields.append('(' + self.argrepr + ')')
226n/a return ' '.join(fields).rstrip()
227n/a
228n/a
229n/adef get_instructions(x, *, first_line=None):
230n/a """Iterator for the opcodes in methods, functions or code
231n/a
232n/a Generates a series of Instruction named tuples giving the details of
233n/a each operations in the supplied code.
234n/a
235n/a If *first_line* is not None, it indicates the line number that should
236n/a be reported for the first source line in the disassembled code.
237n/a Otherwise, the source line information (if any) is taken directly from
238n/a the disassembled code object.
239n/a """
240n/a co = _get_code_object(x)
241n/a cell_names = co.co_cellvars + co.co_freevars
242n/a linestarts = dict(findlinestarts(co))
243n/a if first_line is not None:
244n/a line_offset = first_line - co.co_firstlineno
245n/a else:
246n/a line_offset = 0
247n/a return _get_instructions_bytes(co.co_code, co.co_varnames, co.co_names,
248n/a co.co_consts, cell_names, linestarts,
249n/a line_offset)
250n/a
251n/adef _get_const_info(const_index, const_list):
252n/a """Helper to get optional details about const references
253n/a
254n/a Returns the dereferenced constant and its repr if the constant
255n/a list is defined.
256n/a Otherwise returns the constant index and its repr().
257n/a """
258n/a argval = const_index
259n/a if const_list is not None:
260n/a argval = const_list[const_index]
261n/a return argval, repr(argval)
262n/a
263n/adef _get_name_info(name_index, name_list):
264n/a """Helper to get optional details about named references
265n/a
266n/a Returns the dereferenced name as both value and repr if the name
267n/a list is defined.
268n/a Otherwise returns the name index and its repr().
269n/a """
270n/a argval = name_index
271n/a if name_list is not None:
272n/a argval = name_list[name_index]
273n/a argrepr = argval
274n/a else:
275n/a argrepr = repr(argval)
276n/a return argval, argrepr
277n/a
278n/a
279n/adef _get_instructions_bytes(code, varnames=None, names=None, constants=None,
280n/a cells=None, linestarts=None, line_offset=0):
281n/a """Iterate over the instructions in a bytecode string.
282n/a
283n/a Generates a sequence of Instruction namedtuples giving the details of each
284n/a opcode. Additional information about the code's runtime environment
285n/a (e.g. variable names, constants) can be specified using optional
286n/a arguments.
287n/a
288n/a """
289n/a labels = findlabels(code)
290n/a starts_line = None
291n/a for offset, op, arg in _unpack_opargs(code):
292n/a if linestarts is not None:
293n/a starts_line = linestarts.get(offset, None)
294n/a if starts_line is not None:
295n/a starts_line += line_offset
296n/a is_jump_target = offset in labels
297n/a argval = None
298n/a argrepr = ''
299n/a if arg is not None:
300n/a # Set argval to the dereferenced value of the argument when
301n/a # available, and argrepr to the string representation of argval.
302n/a # _disassemble_bytes needs the string repr of the
303n/a # raw name index for LOAD_GLOBAL, LOAD_CONST, etc.
304n/a argval = arg
305n/a if op in hasconst:
306n/a argval, argrepr = _get_const_info(arg, constants)
307n/a elif op in hasname:
308n/a argval, argrepr = _get_name_info(arg, names)
309n/a elif op in hasjrel:
310n/a argval = offset + 2 + arg
311n/a argrepr = "to " + repr(argval)
312n/a elif op in haslocal:
313n/a argval, argrepr = _get_name_info(arg, varnames)
314n/a elif op in hascompare:
315n/a argval = cmp_op[arg]
316n/a argrepr = argval
317n/a elif op in hasfree:
318n/a argval, argrepr = _get_name_info(arg, cells)
319n/a elif op == FORMAT_VALUE:
320n/a argval = ((None, str, repr, ascii)[arg & 0x3], bool(arg & 0x4))
321n/a argrepr = ('', 'str', 'repr', 'ascii')[arg & 0x3]
322n/a if argval[1]:
323n/a if argrepr:
324n/a argrepr += ', '
325n/a argrepr += 'with format'
326n/a yield Instruction(opname[op], op,
327n/a arg, argval, argrepr,
328n/a offset, starts_line, is_jump_target)
329n/a
330n/adef disassemble(co, lasti=-1, *, file=None):
331n/a """Disassemble a code object."""
332n/a cell_names = co.co_cellvars + co.co_freevars
333n/a linestarts = dict(findlinestarts(co))
334n/a _disassemble_bytes(co.co_code, lasti, co.co_varnames, co.co_names,
335n/a co.co_consts, cell_names, linestarts, file=file)
336n/a
337n/adef _disassemble_bytes(code, lasti=-1, varnames=None, names=None,
338n/a constants=None, cells=None, linestarts=None,
339n/a *, file=None, line_offset=0):
340n/a # Omit the line number column entirely if we have no line number info
341n/a show_lineno = linestarts is not None
342n/a # TODO?: Adjust width upwards if max(linestarts.values()) >= 1000?
343n/a lineno_width = 3 if show_lineno else 0
344n/a for instr in _get_instructions_bytes(code, varnames, names,
345n/a constants, cells, linestarts,
346n/a line_offset=line_offset):
347n/a new_source_line = (show_lineno and
348n/a instr.starts_line is not None and
349n/a instr.offset > 0)
350n/a if new_source_line:
351n/a print(file=file)
352n/a is_current_instr = instr.offset == lasti
353n/a print(instr._disassemble(lineno_width, is_current_instr), file=file)
354n/a
355n/adef _disassemble_str(source, *, file=None):
356n/a """Compile the source string, then disassemble the code object."""
357n/a disassemble(_try_compile(source, '<dis>'), file=file)
358n/a
359n/adisco = disassemble # XXX For backwards compatibility
360n/a
361n/adef _unpack_opargs(code):
362n/a extended_arg = 0
363n/a for i in range(0, len(code), 2):
364n/a op = code[i]
365n/a if op >= HAVE_ARGUMENT:
366n/a arg = code[i+1] | extended_arg
367n/a extended_arg = (arg << 8) if op == EXTENDED_ARG else 0
368n/a else:
369n/a arg = None
370n/a yield (i, op, arg)
371n/a
372n/adef findlabels(code):
373n/a """Detect all offsets in a byte code which are jump targets.
374n/a
375n/a Return the list of offsets.
376n/a
377n/a """
378n/a labels = []
379n/a for offset, op, arg in _unpack_opargs(code):
380n/a if arg is not None:
381n/a if op in hasjrel:
382n/a label = offset + 2 + arg
383n/a elif op in hasjabs:
384n/a label = arg
385n/a else:
386n/a continue
387n/a if label not in labels:
388n/a labels.append(label)
389n/a return labels
390n/a
391n/adef findlinestarts(code):
392n/a """Find the offsets in a byte code which are start of lines in the source.
393n/a
394n/a Generate pairs (offset, lineno) as described in Python/compile.c.
395n/a
396n/a """
397n/a byte_increments = code.co_lnotab[0::2]
398n/a line_increments = code.co_lnotab[1::2]
399n/a
400n/a lastlineno = None
401n/a lineno = code.co_firstlineno
402n/a addr = 0
403n/a for byte_incr, line_incr in zip(byte_increments, line_increments):
404n/a if byte_incr:
405n/a if lineno != lastlineno:
406n/a yield (addr, lineno)
407n/a lastlineno = lineno
408n/a addr += byte_incr
409n/a if line_incr >= 0x80:
410n/a # line_increments is an array of 8-bit signed integers
411n/a line_incr -= 0x100
412n/a lineno += line_incr
413n/a if lineno != lastlineno:
414n/a yield (addr, lineno)
415n/a
416n/aclass Bytecode:
417n/a """The bytecode operations of a piece of code
418n/a
419n/a Instantiate this with a function, method, string of code, or a code object
420n/a (as returned by compile()).
421n/a
422n/a Iterating over this yields the bytecode operations as Instruction instances.
423n/a """
424n/a def __init__(self, x, *, first_line=None, current_offset=None):
425n/a self.codeobj = co = _get_code_object(x)
426n/a if first_line is None:
427n/a self.first_line = co.co_firstlineno
428n/a self._line_offset = 0
429n/a else:
430n/a self.first_line = first_line
431n/a self._line_offset = first_line - co.co_firstlineno
432n/a self._cell_names = co.co_cellvars + co.co_freevars
433n/a self._linestarts = dict(findlinestarts(co))
434n/a self._original_object = x
435n/a self.current_offset = current_offset
436n/a
437n/a def __iter__(self):
438n/a co = self.codeobj
439n/a return _get_instructions_bytes(co.co_code, co.co_varnames, co.co_names,
440n/a co.co_consts, self._cell_names,
441n/a self._linestarts,
442n/a line_offset=self._line_offset)
443n/a
444n/a def __repr__(self):
445n/a return "{}({!r})".format(self.__class__.__name__,
446n/a self._original_object)
447n/a
448n/a @classmethod
449n/a def from_traceback(cls, tb):
450n/a """ Construct a Bytecode from the given traceback """
451n/a while tb.tb_next:
452n/a tb = tb.tb_next
453n/a return cls(tb.tb_frame.f_code, current_offset=tb.tb_lasti)
454n/a
455n/a def info(self):
456n/a """Return formatted information about the code object."""
457n/a return _format_code_info(self.codeobj)
458n/a
459n/a def dis(self):
460n/a """Return a formatted view of the bytecode operations."""
461n/a co = self.codeobj
462n/a if self.current_offset is not None:
463n/a offset = self.current_offset
464n/a else:
465n/a offset = -1
466n/a with io.StringIO() as output:
467n/a _disassemble_bytes(co.co_code, varnames=co.co_varnames,
468n/a names=co.co_names, constants=co.co_consts,
469n/a cells=self._cell_names,
470n/a linestarts=self._linestarts,
471n/a line_offset=self._line_offset,
472n/a file=output,
473n/a lasti=offset)
474n/a return output.getvalue()
475n/a
476n/a
477n/adef _test():
478n/a """Simple test program to disassemble a file."""
479n/a import argparse
480n/a
481n/a parser = argparse.ArgumentParser()
482n/a parser.add_argument('infile', type=argparse.FileType(), nargs='?', default='-')
483n/a args = parser.parse_args()
484n/a with args.infile as infile:
485n/a source = infile.read()
486n/a code = compile(source, args.infile.name, "exec")
487n/a dis(code)
488n/a
489n/aif __name__ == "__main__":
490n/a _test()