xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/dis.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker"""Disassembler of Python byte code into mnemonics."""
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard Workerimport sys
4*cda5da8dSAndroid Build Coastguard Workerimport types
5*cda5da8dSAndroid Build Coastguard Workerimport collections
6*cda5da8dSAndroid Build Coastguard Workerimport io
7*cda5da8dSAndroid Build Coastguard Worker
8*cda5da8dSAndroid Build Coastguard Workerfrom opcode import *
9*cda5da8dSAndroid Build Coastguard Workerfrom opcode import (
10*cda5da8dSAndroid Build Coastguard Worker    __all__ as _opcodes_all,
11*cda5da8dSAndroid Build Coastguard Worker    _cache_format,
12*cda5da8dSAndroid Build Coastguard Worker    _inline_cache_entries,
13*cda5da8dSAndroid Build Coastguard Worker    _nb_ops,
14*cda5da8dSAndroid Build Coastguard Worker    _specializations,
15*cda5da8dSAndroid Build Coastguard Worker    _specialized_instructions,
16*cda5da8dSAndroid Build Coastguard Worker)
17*cda5da8dSAndroid Build Coastguard Worker
18*cda5da8dSAndroid Build Coastguard Worker__all__ = ["code_info", "dis", "disassemble", "distb", "disco",
19*cda5da8dSAndroid Build Coastguard Worker           "findlinestarts", "findlabels", "show_code",
20*cda5da8dSAndroid Build Coastguard Worker           "get_instructions", "Instruction", "Bytecode"] + _opcodes_all
21*cda5da8dSAndroid Build Coastguard Workerdel _opcodes_all
22*cda5da8dSAndroid Build Coastguard Worker
23*cda5da8dSAndroid Build Coastguard Worker_have_code = (types.MethodType, types.FunctionType, types.CodeType,
24*cda5da8dSAndroid Build Coastguard Worker              classmethod, staticmethod, type)
25*cda5da8dSAndroid Build Coastguard Worker
26*cda5da8dSAndroid Build Coastguard WorkerFORMAT_VALUE = opmap['FORMAT_VALUE']
27*cda5da8dSAndroid Build Coastguard WorkerFORMAT_VALUE_CONVERTERS = (
28*cda5da8dSAndroid Build Coastguard Worker    (None, ''),
29*cda5da8dSAndroid Build Coastguard Worker    (str, 'str'),
30*cda5da8dSAndroid Build Coastguard Worker    (repr, 'repr'),
31*cda5da8dSAndroid Build Coastguard Worker    (ascii, 'ascii'),
32*cda5da8dSAndroid Build Coastguard Worker)
33*cda5da8dSAndroid Build Coastguard WorkerMAKE_FUNCTION = opmap['MAKE_FUNCTION']
34*cda5da8dSAndroid Build Coastguard WorkerMAKE_FUNCTION_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure')
35*cda5da8dSAndroid Build Coastguard Worker
36*cda5da8dSAndroid Build Coastguard WorkerLOAD_CONST = opmap['LOAD_CONST']
37*cda5da8dSAndroid Build Coastguard WorkerLOAD_GLOBAL = opmap['LOAD_GLOBAL']
38*cda5da8dSAndroid Build Coastguard WorkerBINARY_OP = opmap['BINARY_OP']
39*cda5da8dSAndroid Build Coastguard WorkerJUMP_BACKWARD = opmap['JUMP_BACKWARD']
40*cda5da8dSAndroid Build Coastguard Worker
41*cda5da8dSAndroid Build Coastguard WorkerCACHE = opmap["CACHE"]
42*cda5da8dSAndroid Build Coastguard Worker
43*cda5da8dSAndroid Build Coastguard Worker_all_opname = list(opname)
44*cda5da8dSAndroid Build Coastguard Worker_all_opmap = dict(opmap)
45*cda5da8dSAndroid Build Coastguard Worker_empty_slot = [slot for slot, name in enumerate(_all_opname) if name.startswith("<")]
46*cda5da8dSAndroid Build Coastguard Workerfor spec_op, specialized in zip(_empty_slot, _specialized_instructions):
47*cda5da8dSAndroid Build Coastguard Worker    # fill opname and opmap
48*cda5da8dSAndroid Build Coastguard Worker    _all_opname[spec_op] = specialized
49*cda5da8dSAndroid Build Coastguard Worker    _all_opmap[specialized] = spec_op
50*cda5da8dSAndroid Build Coastguard Worker
51*cda5da8dSAndroid Build Coastguard Workerdeoptmap = {
52*cda5da8dSAndroid Build Coastguard Worker    specialized: base for base, family in _specializations.items() for specialized in family
53*cda5da8dSAndroid Build Coastguard Worker}
54*cda5da8dSAndroid Build Coastguard Worker
55*cda5da8dSAndroid Build Coastguard Workerdef _try_compile(source, name):
56*cda5da8dSAndroid Build Coastguard Worker    """Attempts to compile the given source, first as an expression and
57*cda5da8dSAndroid Build Coastguard Worker       then as a statement if the first approach fails.
58*cda5da8dSAndroid Build Coastguard Worker
59*cda5da8dSAndroid Build Coastguard Worker       Utility function to accept strings in functions that otherwise
60*cda5da8dSAndroid Build Coastguard Worker       expect code objects
61*cda5da8dSAndroid Build Coastguard Worker    """
62*cda5da8dSAndroid Build Coastguard Worker    try:
63*cda5da8dSAndroid Build Coastguard Worker        c = compile(source, name, 'eval')
64*cda5da8dSAndroid Build Coastguard Worker    except SyntaxError:
65*cda5da8dSAndroid Build Coastguard Worker        c = compile(source, name, 'exec')
66*cda5da8dSAndroid Build Coastguard Worker    return c
67*cda5da8dSAndroid Build Coastguard Worker
68*cda5da8dSAndroid Build Coastguard Workerdef dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False):
69*cda5da8dSAndroid Build Coastguard Worker    """Disassemble classes, methods, functions, and other compiled objects.
70*cda5da8dSAndroid Build Coastguard Worker
71*cda5da8dSAndroid Build Coastguard Worker    With no argument, disassemble the last traceback.
72*cda5da8dSAndroid Build Coastguard Worker
73*cda5da8dSAndroid Build Coastguard Worker    Compiled objects currently include generator objects, async generator
74*cda5da8dSAndroid Build Coastguard Worker    objects, and coroutine objects, all of which store their code object
75*cda5da8dSAndroid Build Coastguard Worker    in a special attribute.
76*cda5da8dSAndroid Build Coastguard Worker    """
77*cda5da8dSAndroid Build Coastguard Worker    if x is None:
78*cda5da8dSAndroid Build Coastguard Worker        distb(file=file, show_caches=show_caches, adaptive=adaptive)
79*cda5da8dSAndroid Build Coastguard Worker        return
80*cda5da8dSAndroid Build Coastguard Worker    # Extract functions from methods.
81*cda5da8dSAndroid Build Coastguard Worker    if hasattr(x, '__func__'):
82*cda5da8dSAndroid Build Coastguard Worker        x = x.__func__
83*cda5da8dSAndroid Build Coastguard Worker    # Extract compiled code objects from...
84*cda5da8dSAndroid Build Coastguard Worker    if hasattr(x, '__code__'):  # ...a function, or
85*cda5da8dSAndroid Build Coastguard Worker        x = x.__code__
86*cda5da8dSAndroid Build Coastguard Worker    elif hasattr(x, 'gi_code'):  #...a generator object, or
87*cda5da8dSAndroid Build Coastguard Worker        x = x.gi_code
88*cda5da8dSAndroid Build Coastguard Worker    elif hasattr(x, 'ag_code'):  #...an asynchronous generator object, or
89*cda5da8dSAndroid Build Coastguard Worker        x = x.ag_code
90*cda5da8dSAndroid Build Coastguard Worker    elif hasattr(x, 'cr_code'):  #...a coroutine.
91*cda5da8dSAndroid Build Coastguard Worker        x = x.cr_code
92*cda5da8dSAndroid Build Coastguard Worker    # Perform the disassembly.
93*cda5da8dSAndroid Build Coastguard Worker    if hasattr(x, '__dict__'):  # Class or module
94*cda5da8dSAndroid Build Coastguard Worker        items = sorted(x.__dict__.items())
95*cda5da8dSAndroid Build Coastguard Worker        for name, x1 in items:
96*cda5da8dSAndroid Build Coastguard Worker            if isinstance(x1, _have_code):
97*cda5da8dSAndroid Build Coastguard Worker                print("Disassembly of %s:" % name, file=file)
98*cda5da8dSAndroid Build Coastguard Worker                try:
99*cda5da8dSAndroid Build Coastguard Worker                    dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive)
100*cda5da8dSAndroid Build Coastguard Worker                except TypeError as msg:
101*cda5da8dSAndroid Build Coastguard Worker                    print("Sorry:", msg, file=file)
102*cda5da8dSAndroid Build Coastguard Worker                print(file=file)
103*cda5da8dSAndroid Build Coastguard Worker    elif hasattr(x, 'co_code'): # Code object
104*cda5da8dSAndroid Build Coastguard Worker        _disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive)
105*cda5da8dSAndroid Build Coastguard Worker    elif isinstance(x, (bytes, bytearray)): # Raw bytecode
106*cda5da8dSAndroid Build Coastguard Worker        _disassemble_bytes(x, file=file, show_caches=show_caches)
107*cda5da8dSAndroid Build Coastguard Worker    elif isinstance(x, str):    # Source code
108*cda5da8dSAndroid Build Coastguard Worker        _disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive)
109*cda5da8dSAndroid Build Coastguard Worker    else:
110*cda5da8dSAndroid Build Coastguard Worker        raise TypeError("don't know how to disassemble %s objects" %
111*cda5da8dSAndroid Build Coastguard Worker                        type(x).__name__)
112*cda5da8dSAndroid Build Coastguard Worker
113*cda5da8dSAndroid Build Coastguard Workerdef distb(tb=None, *, file=None, show_caches=False, adaptive=False):
114*cda5da8dSAndroid Build Coastguard Worker    """Disassemble a traceback (default: last traceback)."""
115*cda5da8dSAndroid Build Coastguard Worker    if tb is None:
116*cda5da8dSAndroid Build Coastguard Worker        try:
117*cda5da8dSAndroid Build Coastguard Worker            tb = sys.last_traceback
118*cda5da8dSAndroid Build Coastguard Worker        except AttributeError:
119*cda5da8dSAndroid Build Coastguard Worker            raise RuntimeError("no last traceback to disassemble") from None
120*cda5da8dSAndroid Build Coastguard Worker        while tb.tb_next: tb = tb.tb_next
121*cda5da8dSAndroid Build Coastguard Worker    disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file, show_caches=show_caches, adaptive=adaptive)
122*cda5da8dSAndroid Build Coastguard Worker
123*cda5da8dSAndroid Build Coastguard Worker# The inspect module interrogates this dictionary to build its
124*cda5da8dSAndroid Build Coastguard Worker# list of CO_* constants. It is also used by pretty_flags to
125*cda5da8dSAndroid Build Coastguard Worker# turn the co_flags field into a human readable list.
126*cda5da8dSAndroid Build Coastguard WorkerCOMPILER_FLAG_NAMES = {
127*cda5da8dSAndroid Build Coastguard Worker     1: "OPTIMIZED",
128*cda5da8dSAndroid Build Coastguard Worker     2: "NEWLOCALS",
129*cda5da8dSAndroid Build Coastguard Worker     4: "VARARGS",
130*cda5da8dSAndroid Build Coastguard Worker     8: "VARKEYWORDS",
131*cda5da8dSAndroid Build Coastguard Worker    16: "NESTED",
132*cda5da8dSAndroid Build Coastguard Worker    32: "GENERATOR",
133*cda5da8dSAndroid Build Coastguard Worker    64: "NOFREE",
134*cda5da8dSAndroid Build Coastguard Worker   128: "COROUTINE",
135*cda5da8dSAndroid Build Coastguard Worker   256: "ITERABLE_COROUTINE",
136*cda5da8dSAndroid Build Coastguard Worker   512: "ASYNC_GENERATOR",
137*cda5da8dSAndroid Build Coastguard Worker}
138*cda5da8dSAndroid Build Coastguard Worker
139*cda5da8dSAndroid Build Coastguard Workerdef pretty_flags(flags):
140*cda5da8dSAndroid Build Coastguard Worker    """Return pretty representation of code flags."""
141*cda5da8dSAndroid Build Coastguard Worker    names = []
142*cda5da8dSAndroid Build Coastguard Worker    for i in range(32):
143*cda5da8dSAndroid Build Coastguard Worker        flag = 1<<i
144*cda5da8dSAndroid Build Coastguard Worker        if flags & flag:
145*cda5da8dSAndroid Build Coastguard Worker            names.append(COMPILER_FLAG_NAMES.get(flag, hex(flag)))
146*cda5da8dSAndroid Build Coastguard Worker            flags ^= flag
147*cda5da8dSAndroid Build Coastguard Worker            if not flags:
148*cda5da8dSAndroid Build Coastguard Worker                break
149*cda5da8dSAndroid Build Coastguard Worker    else:
150*cda5da8dSAndroid Build Coastguard Worker        names.append(hex(flags))
151*cda5da8dSAndroid Build Coastguard Worker    return ", ".join(names)
152*cda5da8dSAndroid Build Coastguard Worker
153*cda5da8dSAndroid Build Coastguard Workerclass _Unknown:
154*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
155*cda5da8dSAndroid Build Coastguard Worker        return "<unknown>"
156*cda5da8dSAndroid Build Coastguard Worker
157*cda5da8dSAndroid Build Coastguard Worker# Sentinel to represent values that cannot be calculated
158*cda5da8dSAndroid Build Coastguard WorkerUNKNOWN = _Unknown()
159*cda5da8dSAndroid Build Coastguard Worker
160*cda5da8dSAndroid Build Coastguard Workerdef _get_code_object(x):
161*cda5da8dSAndroid Build Coastguard Worker    """Helper to handle methods, compiled or raw code objects, and strings."""
162*cda5da8dSAndroid Build Coastguard Worker    # Extract functions from methods.
163*cda5da8dSAndroid Build Coastguard Worker    if hasattr(x, '__func__'):
164*cda5da8dSAndroid Build Coastguard Worker        x = x.__func__
165*cda5da8dSAndroid Build Coastguard Worker    # Extract compiled code objects from...
166*cda5da8dSAndroid Build Coastguard Worker    if hasattr(x, '__code__'):  # ...a function, or
167*cda5da8dSAndroid Build Coastguard Worker        x = x.__code__
168*cda5da8dSAndroid Build Coastguard Worker    elif hasattr(x, 'gi_code'):  #...a generator object, or
169*cda5da8dSAndroid Build Coastguard Worker        x = x.gi_code
170*cda5da8dSAndroid Build Coastguard Worker    elif hasattr(x, 'ag_code'):  #...an asynchronous generator object, or
171*cda5da8dSAndroid Build Coastguard Worker        x = x.ag_code
172*cda5da8dSAndroid Build Coastguard Worker    elif hasattr(x, 'cr_code'):  #...a coroutine.
173*cda5da8dSAndroid Build Coastguard Worker        x = x.cr_code
174*cda5da8dSAndroid Build Coastguard Worker    # Handle source code.
175*cda5da8dSAndroid Build Coastguard Worker    if isinstance(x, str):
176*cda5da8dSAndroid Build Coastguard Worker        x = _try_compile(x, "<disassembly>")
177*cda5da8dSAndroid Build Coastguard Worker    # By now, if we don't have a code object, we can't disassemble x.
178*cda5da8dSAndroid Build Coastguard Worker    if hasattr(x, 'co_code'):
179*cda5da8dSAndroid Build Coastguard Worker        return x
180*cda5da8dSAndroid Build Coastguard Worker    raise TypeError("don't know how to disassemble %s objects" %
181*cda5da8dSAndroid Build Coastguard Worker                    type(x).__name__)
182*cda5da8dSAndroid Build Coastguard Worker
183*cda5da8dSAndroid Build Coastguard Workerdef _deoptop(op):
184*cda5da8dSAndroid Build Coastguard Worker    name = _all_opname[op]
185*cda5da8dSAndroid Build Coastguard Worker    return _all_opmap[deoptmap[name]] if name in deoptmap else op
186*cda5da8dSAndroid Build Coastguard Worker
187*cda5da8dSAndroid Build Coastguard Workerdef _get_code_array(co, adaptive):
188*cda5da8dSAndroid Build Coastguard Worker    return co._co_code_adaptive if adaptive else co.co_code
189*cda5da8dSAndroid Build Coastguard Worker
190*cda5da8dSAndroid Build Coastguard Workerdef code_info(x):
191*cda5da8dSAndroid Build Coastguard Worker    """Formatted details of methods, functions, or code."""
192*cda5da8dSAndroid Build Coastguard Worker    return _format_code_info(_get_code_object(x))
193*cda5da8dSAndroid Build Coastguard Worker
194*cda5da8dSAndroid Build Coastguard Workerdef _format_code_info(co):
195*cda5da8dSAndroid Build Coastguard Worker    lines = []
196*cda5da8dSAndroid Build Coastguard Worker    lines.append("Name:              %s" % co.co_name)
197*cda5da8dSAndroid Build Coastguard Worker    lines.append("Filename:          %s" % co.co_filename)
198*cda5da8dSAndroid Build Coastguard Worker    lines.append("Argument count:    %s" % co.co_argcount)
199*cda5da8dSAndroid Build Coastguard Worker    lines.append("Positional-only arguments: %s" % co.co_posonlyargcount)
200*cda5da8dSAndroid Build Coastguard Worker    lines.append("Kw-only arguments: %s" % co.co_kwonlyargcount)
201*cda5da8dSAndroid Build Coastguard Worker    lines.append("Number of locals:  %s" % co.co_nlocals)
202*cda5da8dSAndroid Build Coastguard Worker    lines.append("Stack size:        %s" % co.co_stacksize)
203*cda5da8dSAndroid Build Coastguard Worker    lines.append("Flags:             %s" % pretty_flags(co.co_flags))
204*cda5da8dSAndroid Build Coastguard Worker    if co.co_consts:
205*cda5da8dSAndroid Build Coastguard Worker        lines.append("Constants:")
206*cda5da8dSAndroid Build Coastguard Worker        for i_c in enumerate(co.co_consts):
207*cda5da8dSAndroid Build Coastguard Worker            lines.append("%4d: %r" % i_c)
208*cda5da8dSAndroid Build Coastguard Worker    if co.co_names:
209*cda5da8dSAndroid Build Coastguard Worker        lines.append("Names:")
210*cda5da8dSAndroid Build Coastguard Worker        for i_n in enumerate(co.co_names):
211*cda5da8dSAndroid Build Coastguard Worker            lines.append("%4d: %s" % i_n)
212*cda5da8dSAndroid Build Coastguard Worker    if co.co_varnames:
213*cda5da8dSAndroid Build Coastguard Worker        lines.append("Variable names:")
214*cda5da8dSAndroid Build Coastguard Worker        for i_n in enumerate(co.co_varnames):
215*cda5da8dSAndroid Build Coastguard Worker            lines.append("%4d: %s" % i_n)
216*cda5da8dSAndroid Build Coastguard Worker    if co.co_freevars:
217*cda5da8dSAndroid Build Coastguard Worker        lines.append("Free variables:")
218*cda5da8dSAndroid Build Coastguard Worker        for i_n in enumerate(co.co_freevars):
219*cda5da8dSAndroid Build Coastguard Worker            lines.append("%4d: %s" % i_n)
220*cda5da8dSAndroid Build Coastguard Worker    if co.co_cellvars:
221*cda5da8dSAndroid Build Coastguard Worker        lines.append("Cell variables:")
222*cda5da8dSAndroid Build Coastguard Worker        for i_n in enumerate(co.co_cellvars):
223*cda5da8dSAndroid Build Coastguard Worker            lines.append("%4d: %s" % i_n)
224*cda5da8dSAndroid Build Coastguard Worker    return "\n".join(lines)
225*cda5da8dSAndroid Build Coastguard Worker
226*cda5da8dSAndroid Build Coastguard Workerdef show_code(co, *, file=None):
227*cda5da8dSAndroid Build Coastguard Worker    """Print details of methods, functions, or code to *file*.
228*cda5da8dSAndroid Build Coastguard Worker
229*cda5da8dSAndroid Build Coastguard Worker    If *file* is not provided, the output is printed on stdout.
230*cda5da8dSAndroid Build Coastguard Worker    """
231*cda5da8dSAndroid Build Coastguard Worker    print(code_info(co), file=file)
232*cda5da8dSAndroid Build Coastguard Worker
233*cda5da8dSAndroid Build Coastguard WorkerPositions = collections.namedtuple(
234*cda5da8dSAndroid Build Coastguard Worker    'Positions',
235*cda5da8dSAndroid Build Coastguard Worker    [
236*cda5da8dSAndroid Build Coastguard Worker        'lineno',
237*cda5da8dSAndroid Build Coastguard Worker        'end_lineno',
238*cda5da8dSAndroid Build Coastguard Worker        'col_offset',
239*cda5da8dSAndroid Build Coastguard Worker        'end_col_offset',
240*cda5da8dSAndroid Build Coastguard Worker    ],
241*cda5da8dSAndroid Build Coastguard Worker    defaults=[None] * 4
242*cda5da8dSAndroid Build Coastguard Worker)
243*cda5da8dSAndroid Build Coastguard Worker
244*cda5da8dSAndroid Build Coastguard Worker_Instruction = collections.namedtuple(
245*cda5da8dSAndroid Build Coastguard Worker    "_Instruction",
246*cda5da8dSAndroid Build Coastguard Worker    [
247*cda5da8dSAndroid Build Coastguard Worker        'opname',
248*cda5da8dSAndroid Build Coastguard Worker        'opcode',
249*cda5da8dSAndroid Build Coastguard Worker        'arg',
250*cda5da8dSAndroid Build Coastguard Worker        'argval',
251*cda5da8dSAndroid Build Coastguard Worker        'argrepr',
252*cda5da8dSAndroid Build Coastguard Worker        'offset',
253*cda5da8dSAndroid Build Coastguard Worker        'starts_line',
254*cda5da8dSAndroid Build Coastguard Worker        'is_jump_target',
255*cda5da8dSAndroid Build Coastguard Worker        'positions'
256*cda5da8dSAndroid Build Coastguard Worker    ],
257*cda5da8dSAndroid Build Coastguard Worker    defaults=[None]
258*cda5da8dSAndroid Build Coastguard Worker)
259*cda5da8dSAndroid Build Coastguard Worker
260*cda5da8dSAndroid Build Coastguard Worker_Instruction.opname.__doc__ = "Human readable name for operation"
261*cda5da8dSAndroid Build Coastguard Worker_Instruction.opcode.__doc__ = "Numeric code for operation"
262*cda5da8dSAndroid Build Coastguard Worker_Instruction.arg.__doc__ = "Numeric argument to operation (if any), otherwise None"
263*cda5da8dSAndroid Build Coastguard Worker_Instruction.argval.__doc__ = "Resolved arg value (if known), otherwise same as arg"
264*cda5da8dSAndroid Build Coastguard Worker_Instruction.argrepr.__doc__ = "Human readable description of operation argument"
265*cda5da8dSAndroid Build Coastguard Worker_Instruction.offset.__doc__ = "Start index of operation within bytecode sequence"
266*cda5da8dSAndroid Build Coastguard Worker_Instruction.starts_line.__doc__ = "Line started by this opcode (if any), otherwise None"
267*cda5da8dSAndroid Build Coastguard Worker_Instruction.is_jump_target.__doc__ = "True if other code jumps to here, otherwise False"
268*cda5da8dSAndroid Build Coastguard Worker_Instruction.positions.__doc__ = "dis.Positions object holding the span of source code covered by this instruction"
269*cda5da8dSAndroid Build Coastguard Worker
270*cda5da8dSAndroid Build Coastguard Worker_ExceptionTableEntry = collections.namedtuple("_ExceptionTableEntry",
271*cda5da8dSAndroid Build Coastguard Worker    "start end target depth lasti")
272*cda5da8dSAndroid Build Coastguard Worker
273*cda5da8dSAndroid Build Coastguard Worker_OPNAME_WIDTH = 20
274*cda5da8dSAndroid Build Coastguard Worker_OPARG_WIDTH = 5
275*cda5da8dSAndroid Build Coastguard Worker
276*cda5da8dSAndroid Build Coastguard Workerclass Instruction(_Instruction):
277*cda5da8dSAndroid Build Coastguard Worker    """Details for a bytecode operation
278*cda5da8dSAndroid Build Coastguard Worker
279*cda5da8dSAndroid Build Coastguard Worker       Defined fields:
280*cda5da8dSAndroid Build Coastguard Worker         opname - human readable name for operation
281*cda5da8dSAndroid Build Coastguard Worker         opcode - numeric code for operation
282*cda5da8dSAndroid Build Coastguard Worker         arg - numeric argument to operation (if any), otherwise None
283*cda5da8dSAndroid Build Coastguard Worker         argval - resolved arg value (if known), otherwise same as arg
284*cda5da8dSAndroid Build Coastguard Worker         argrepr - human readable description of operation argument
285*cda5da8dSAndroid Build Coastguard Worker         offset - start index of operation within bytecode sequence
286*cda5da8dSAndroid Build Coastguard Worker         starts_line - line started by this opcode (if any), otherwise None
287*cda5da8dSAndroid Build Coastguard Worker         is_jump_target - True if other code jumps to here, otherwise False
288*cda5da8dSAndroid Build Coastguard Worker         positions - Optional dis.Positions object holding the span of source code
289*cda5da8dSAndroid Build Coastguard Worker                     covered by this instruction
290*cda5da8dSAndroid Build Coastguard Worker    """
291*cda5da8dSAndroid Build Coastguard Worker
292*cda5da8dSAndroid Build Coastguard Worker    def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=4):
293*cda5da8dSAndroid Build Coastguard Worker        """Format instruction details for inclusion in disassembly output
294*cda5da8dSAndroid Build Coastguard Worker
295*cda5da8dSAndroid Build Coastguard Worker        *lineno_width* sets the width of the line number field (0 omits it)
296*cda5da8dSAndroid Build Coastguard Worker        *mark_as_current* inserts a '-->' marker arrow as part of the line
297*cda5da8dSAndroid Build Coastguard Worker        *offset_width* sets the width of the instruction offset field
298*cda5da8dSAndroid Build Coastguard Worker        """
299*cda5da8dSAndroid Build Coastguard Worker        fields = []
300*cda5da8dSAndroid Build Coastguard Worker        # Column: Source code line number
301*cda5da8dSAndroid Build Coastguard Worker        if lineno_width:
302*cda5da8dSAndroid Build Coastguard Worker            if self.starts_line is not None:
303*cda5da8dSAndroid Build Coastguard Worker                lineno_fmt = "%%%dd" % lineno_width
304*cda5da8dSAndroid Build Coastguard Worker                fields.append(lineno_fmt % self.starts_line)
305*cda5da8dSAndroid Build Coastguard Worker            else:
306*cda5da8dSAndroid Build Coastguard Worker                fields.append(' ' * lineno_width)
307*cda5da8dSAndroid Build Coastguard Worker        # Column: Current instruction indicator
308*cda5da8dSAndroid Build Coastguard Worker        if mark_as_current:
309*cda5da8dSAndroid Build Coastguard Worker            fields.append('-->')
310*cda5da8dSAndroid Build Coastguard Worker        else:
311*cda5da8dSAndroid Build Coastguard Worker            fields.append('   ')
312*cda5da8dSAndroid Build Coastguard Worker        # Column: Jump target marker
313*cda5da8dSAndroid Build Coastguard Worker        if self.is_jump_target:
314*cda5da8dSAndroid Build Coastguard Worker            fields.append('>>')
315*cda5da8dSAndroid Build Coastguard Worker        else:
316*cda5da8dSAndroid Build Coastguard Worker            fields.append('  ')
317*cda5da8dSAndroid Build Coastguard Worker        # Column: Instruction offset from start of code sequence
318*cda5da8dSAndroid Build Coastguard Worker        fields.append(repr(self.offset).rjust(offset_width))
319*cda5da8dSAndroid Build Coastguard Worker        # Column: Opcode name
320*cda5da8dSAndroid Build Coastguard Worker        fields.append(self.opname.ljust(_OPNAME_WIDTH))
321*cda5da8dSAndroid Build Coastguard Worker        # Column: Opcode argument
322*cda5da8dSAndroid Build Coastguard Worker        if self.arg is not None:
323*cda5da8dSAndroid Build Coastguard Worker            fields.append(repr(self.arg).rjust(_OPARG_WIDTH))
324*cda5da8dSAndroid Build Coastguard Worker            # Column: Opcode argument details
325*cda5da8dSAndroid Build Coastguard Worker            if self.argrepr:
326*cda5da8dSAndroid Build Coastguard Worker                fields.append('(' + self.argrepr + ')')
327*cda5da8dSAndroid Build Coastguard Worker        return ' '.join(fields).rstrip()
328*cda5da8dSAndroid Build Coastguard Worker
329*cda5da8dSAndroid Build Coastguard Worker
330*cda5da8dSAndroid Build Coastguard Workerdef get_instructions(x, *, first_line=None, show_caches=False, adaptive=False):
331*cda5da8dSAndroid Build Coastguard Worker    """Iterator for the opcodes in methods, functions or code
332*cda5da8dSAndroid Build Coastguard Worker
333*cda5da8dSAndroid Build Coastguard Worker    Generates a series of Instruction named tuples giving the details of
334*cda5da8dSAndroid Build Coastguard Worker    each operations in the supplied code.
335*cda5da8dSAndroid Build Coastguard Worker
336*cda5da8dSAndroid Build Coastguard Worker    If *first_line* is not None, it indicates the line number that should
337*cda5da8dSAndroid Build Coastguard Worker    be reported for the first source line in the disassembled code.
338*cda5da8dSAndroid Build Coastguard Worker    Otherwise, the source line information (if any) is taken directly from
339*cda5da8dSAndroid Build Coastguard Worker    the disassembled code object.
340*cda5da8dSAndroid Build Coastguard Worker    """
341*cda5da8dSAndroid Build Coastguard Worker    co = _get_code_object(x)
342*cda5da8dSAndroid Build Coastguard Worker    linestarts = dict(findlinestarts(co))
343*cda5da8dSAndroid Build Coastguard Worker    if first_line is not None:
344*cda5da8dSAndroid Build Coastguard Worker        line_offset = first_line - co.co_firstlineno
345*cda5da8dSAndroid Build Coastguard Worker    else:
346*cda5da8dSAndroid Build Coastguard Worker        line_offset = 0
347*cda5da8dSAndroid Build Coastguard Worker    return _get_instructions_bytes(_get_code_array(co, adaptive),
348*cda5da8dSAndroid Build Coastguard Worker                                   co._varname_from_oparg,
349*cda5da8dSAndroid Build Coastguard Worker                                   co.co_names, co.co_consts,
350*cda5da8dSAndroid Build Coastguard Worker                                   linestarts, line_offset,
351*cda5da8dSAndroid Build Coastguard Worker                                   co_positions=co.co_positions(),
352*cda5da8dSAndroid Build Coastguard Worker                                   show_caches=show_caches)
353*cda5da8dSAndroid Build Coastguard Worker
354*cda5da8dSAndroid Build Coastguard Workerdef _get_const_value(op, arg, co_consts):
355*cda5da8dSAndroid Build Coastguard Worker    """Helper to get the value of the const in a hasconst op.
356*cda5da8dSAndroid Build Coastguard Worker
357*cda5da8dSAndroid Build Coastguard Worker       Returns the dereferenced constant if this is possible.
358*cda5da8dSAndroid Build Coastguard Worker       Otherwise (if it is a LOAD_CONST and co_consts is not
359*cda5da8dSAndroid Build Coastguard Worker       provided) returns the dis.UNKNOWN sentinel.
360*cda5da8dSAndroid Build Coastguard Worker    """
361*cda5da8dSAndroid Build Coastguard Worker    assert op in hasconst
362*cda5da8dSAndroid Build Coastguard Worker
363*cda5da8dSAndroid Build Coastguard Worker    argval = UNKNOWN
364*cda5da8dSAndroid Build Coastguard Worker    if op == LOAD_CONST:
365*cda5da8dSAndroid Build Coastguard Worker        if co_consts is not None:
366*cda5da8dSAndroid Build Coastguard Worker            argval = co_consts[arg]
367*cda5da8dSAndroid Build Coastguard Worker    return argval
368*cda5da8dSAndroid Build Coastguard Worker
369*cda5da8dSAndroid Build Coastguard Workerdef _get_const_info(op, arg, co_consts):
370*cda5da8dSAndroid Build Coastguard Worker    """Helper to get optional details about const references
371*cda5da8dSAndroid Build Coastguard Worker
372*cda5da8dSAndroid Build Coastguard Worker       Returns the dereferenced constant and its repr if the value
373*cda5da8dSAndroid Build Coastguard Worker       can be calculated.
374*cda5da8dSAndroid Build Coastguard Worker       Otherwise returns the sentinel value dis.UNKNOWN for the value
375*cda5da8dSAndroid Build Coastguard Worker       and an empty string for its repr.
376*cda5da8dSAndroid Build Coastguard Worker    """
377*cda5da8dSAndroid Build Coastguard Worker    argval = _get_const_value(op, arg, co_consts)
378*cda5da8dSAndroid Build Coastguard Worker    argrepr = repr(argval) if argval is not UNKNOWN else ''
379*cda5da8dSAndroid Build Coastguard Worker    return argval, argrepr
380*cda5da8dSAndroid Build Coastguard Worker
381*cda5da8dSAndroid Build Coastguard Workerdef _get_name_info(name_index, get_name, **extrainfo):
382*cda5da8dSAndroid Build Coastguard Worker    """Helper to get optional details about named references
383*cda5da8dSAndroid Build Coastguard Worker
384*cda5da8dSAndroid Build Coastguard Worker       Returns the dereferenced name as both value and repr if the name
385*cda5da8dSAndroid Build Coastguard Worker       list is defined.
386*cda5da8dSAndroid Build Coastguard Worker       Otherwise returns the sentinel value dis.UNKNOWN for the value
387*cda5da8dSAndroid Build Coastguard Worker       and an empty string for its repr.
388*cda5da8dSAndroid Build Coastguard Worker    """
389*cda5da8dSAndroid Build Coastguard Worker    if get_name is not None:
390*cda5da8dSAndroid Build Coastguard Worker        argval = get_name(name_index, **extrainfo)
391*cda5da8dSAndroid Build Coastguard Worker        return argval, argval
392*cda5da8dSAndroid Build Coastguard Worker    else:
393*cda5da8dSAndroid Build Coastguard Worker        return UNKNOWN, ''
394*cda5da8dSAndroid Build Coastguard Worker
395*cda5da8dSAndroid Build Coastguard Workerdef _parse_varint(iterator):
396*cda5da8dSAndroid Build Coastguard Worker    b = next(iterator)
397*cda5da8dSAndroid Build Coastguard Worker    val = b & 63
398*cda5da8dSAndroid Build Coastguard Worker    while b&64:
399*cda5da8dSAndroid Build Coastguard Worker        val <<= 6
400*cda5da8dSAndroid Build Coastguard Worker        b = next(iterator)
401*cda5da8dSAndroid Build Coastguard Worker        val |= b&63
402*cda5da8dSAndroid Build Coastguard Worker    return val
403*cda5da8dSAndroid Build Coastguard Worker
404*cda5da8dSAndroid Build Coastguard Workerdef _parse_exception_table(code):
405*cda5da8dSAndroid Build Coastguard Worker    iterator = iter(code.co_exceptiontable)
406*cda5da8dSAndroid Build Coastguard Worker    entries = []
407*cda5da8dSAndroid Build Coastguard Worker    try:
408*cda5da8dSAndroid Build Coastguard Worker        while True:
409*cda5da8dSAndroid Build Coastguard Worker            start = _parse_varint(iterator)*2
410*cda5da8dSAndroid Build Coastguard Worker            length = _parse_varint(iterator)*2
411*cda5da8dSAndroid Build Coastguard Worker            end = start + length
412*cda5da8dSAndroid Build Coastguard Worker            target = _parse_varint(iterator)*2
413*cda5da8dSAndroid Build Coastguard Worker            dl = _parse_varint(iterator)
414*cda5da8dSAndroid Build Coastguard Worker            depth = dl >> 1
415*cda5da8dSAndroid Build Coastguard Worker            lasti = bool(dl&1)
416*cda5da8dSAndroid Build Coastguard Worker            entries.append(_ExceptionTableEntry(start, end, target, depth, lasti))
417*cda5da8dSAndroid Build Coastguard Worker    except StopIteration:
418*cda5da8dSAndroid Build Coastguard Worker        return entries
419*cda5da8dSAndroid Build Coastguard Worker
420*cda5da8dSAndroid Build Coastguard Workerdef _is_backward_jump(op):
421*cda5da8dSAndroid Build Coastguard Worker    return 'JUMP_BACKWARD' in opname[op]
422*cda5da8dSAndroid Build Coastguard Worker
423*cda5da8dSAndroid Build Coastguard Workerdef _get_instructions_bytes(code, varname_from_oparg=None,
424*cda5da8dSAndroid Build Coastguard Worker                            names=None, co_consts=None,
425*cda5da8dSAndroid Build Coastguard Worker                            linestarts=None, line_offset=0,
426*cda5da8dSAndroid Build Coastguard Worker                            exception_entries=(), co_positions=None,
427*cda5da8dSAndroid Build Coastguard Worker                            show_caches=False):
428*cda5da8dSAndroid Build Coastguard Worker    """Iterate over the instructions in a bytecode string.
429*cda5da8dSAndroid Build Coastguard Worker
430*cda5da8dSAndroid Build Coastguard Worker    Generates a sequence of Instruction namedtuples giving the details of each
431*cda5da8dSAndroid Build Coastguard Worker    opcode.  Additional information about the code's runtime environment
432*cda5da8dSAndroid Build Coastguard Worker    (e.g. variable names, co_consts) can be specified using optional
433*cda5da8dSAndroid Build Coastguard Worker    arguments.
434*cda5da8dSAndroid Build Coastguard Worker
435*cda5da8dSAndroid Build Coastguard Worker    """
436*cda5da8dSAndroid Build Coastguard Worker    co_positions = co_positions or iter(())
437*cda5da8dSAndroid Build Coastguard Worker    get_name = None if names is None else names.__getitem__
438*cda5da8dSAndroid Build Coastguard Worker    labels = set(findlabels(code))
439*cda5da8dSAndroid Build Coastguard Worker    for start, end, target, _, _ in exception_entries:
440*cda5da8dSAndroid Build Coastguard Worker        for i in range(start, end):
441*cda5da8dSAndroid Build Coastguard Worker            labels.add(target)
442*cda5da8dSAndroid Build Coastguard Worker    starts_line = None
443*cda5da8dSAndroid Build Coastguard Worker    for offset, op, arg in _unpack_opargs(code):
444*cda5da8dSAndroid Build Coastguard Worker        if linestarts is not None:
445*cda5da8dSAndroid Build Coastguard Worker            starts_line = linestarts.get(offset, None)
446*cda5da8dSAndroid Build Coastguard Worker            if starts_line is not None:
447*cda5da8dSAndroid Build Coastguard Worker                starts_line += line_offset
448*cda5da8dSAndroid Build Coastguard Worker        is_jump_target = offset in labels
449*cda5da8dSAndroid Build Coastguard Worker        argval = None
450*cda5da8dSAndroid Build Coastguard Worker        argrepr = ''
451*cda5da8dSAndroid Build Coastguard Worker        positions = Positions(*next(co_positions, ()))
452*cda5da8dSAndroid Build Coastguard Worker        deop = _deoptop(op)
453*cda5da8dSAndroid Build Coastguard Worker        if arg is not None:
454*cda5da8dSAndroid Build Coastguard Worker            #  Set argval to the dereferenced value of the argument when
455*cda5da8dSAndroid Build Coastguard Worker            #  available, and argrepr to the string representation of argval.
456*cda5da8dSAndroid Build Coastguard Worker            #    _disassemble_bytes needs the string repr of the
457*cda5da8dSAndroid Build Coastguard Worker            #    raw name index for LOAD_GLOBAL, LOAD_CONST, etc.
458*cda5da8dSAndroid Build Coastguard Worker            argval = arg
459*cda5da8dSAndroid Build Coastguard Worker            if deop in hasconst:
460*cda5da8dSAndroid Build Coastguard Worker                argval, argrepr = _get_const_info(deop, arg, co_consts)
461*cda5da8dSAndroid Build Coastguard Worker            elif deop in hasname:
462*cda5da8dSAndroid Build Coastguard Worker                if deop == LOAD_GLOBAL:
463*cda5da8dSAndroid Build Coastguard Worker                    argval, argrepr = _get_name_info(arg//2, get_name)
464*cda5da8dSAndroid Build Coastguard Worker                    if (arg & 1) and argrepr:
465*cda5da8dSAndroid Build Coastguard Worker                        argrepr = "NULL + " + argrepr
466*cda5da8dSAndroid Build Coastguard Worker                else:
467*cda5da8dSAndroid Build Coastguard Worker                    argval, argrepr = _get_name_info(arg, get_name)
468*cda5da8dSAndroid Build Coastguard Worker            elif deop in hasjabs:
469*cda5da8dSAndroid Build Coastguard Worker                argval = arg*2
470*cda5da8dSAndroid Build Coastguard Worker                argrepr = "to " + repr(argval)
471*cda5da8dSAndroid Build Coastguard Worker            elif deop in hasjrel:
472*cda5da8dSAndroid Build Coastguard Worker                signed_arg = -arg if _is_backward_jump(deop) else arg
473*cda5da8dSAndroid Build Coastguard Worker                argval = offset + 2 + signed_arg*2
474*cda5da8dSAndroid Build Coastguard Worker                argrepr = "to " + repr(argval)
475*cda5da8dSAndroid Build Coastguard Worker            elif deop in haslocal or deop in hasfree:
476*cda5da8dSAndroid Build Coastguard Worker                argval, argrepr = _get_name_info(arg, varname_from_oparg)
477*cda5da8dSAndroid Build Coastguard Worker            elif deop in hascompare:
478*cda5da8dSAndroid Build Coastguard Worker                argval = cmp_op[arg]
479*cda5da8dSAndroid Build Coastguard Worker                argrepr = argval
480*cda5da8dSAndroid Build Coastguard Worker            elif deop == FORMAT_VALUE:
481*cda5da8dSAndroid Build Coastguard Worker                argval, argrepr = FORMAT_VALUE_CONVERTERS[arg & 0x3]
482*cda5da8dSAndroid Build Coastguard Worker                argval = (argval, bool(arg & 0x4))
483*cda5da8dSAndroid Build Coastguard Worker                if argval[1]:
484*cda5da8dSAndroid Build Coastguard Worker                    if argrepr:
485*cda5da8dSAndroid Build Coastguard Worker                        argrepr += ', '
486*cda5da8dSAndroid Build Coastguard Worker                    argrepr += 'with format'
487*cda5da8dSAndroid Build Coastguard Worker            elif deop == MAKE_FUNCTION:
488*cda5da8dSAndroid Build Coastguard Worker                argrepr = ', '.join(s for i, s in enumerate(MAKE_FUNCTION_FLAGS)
489*cda5da8dSAndroid Build Coastguard Worker                                    if arg & (1<<i))
490*cda5da8dSAndroid Build Coastguard Worker            elif deop == BINARY_OP:
491*cda5da8dSAndroid Build Coastguard Worker                _, argrepr = _nb_ops[arg]
492*cda5da8dSAndroid Build Coastguard Worker        yield Instruction(_all_opname[op], op,
493*cda5da8dSAndroid Build Coastguard Worker                          arg, argval, argrepr,
494*cda5da8dSAndroid Build Coastguard Worker                          offset, starts_line, is_jump_target, positions)
495*cda5da8dSAndroid Build Coastguard Worker        caches = _inline_cache_entries[deop]
496*cda5da8dSAndroid Build Coastguard Worker        if not caches:
497*cda5da8dSAndroid Build Coastguard Worker            continue
498*cda5da8dSAndroid Build Coastguard Worker        if not show_caches:
499*cda5da8dSAndroid Build Coastguard Worker            # We still need to advance the co_positions iterator:
500*cda5da8dSAndroid Build Coastguard Worker            for _ in range(caches):
501*cda5da8dSAndroid Build Coastguard Worker                next(co_positions, ())
502*cda5da8dSAndroid Build Coastguard Worker            continue
503*cda5da8dSAndroid Build Coastguard Worker        for name, size in _cache_format[opname[deop]].items():
504*cda5da8dSAndroid Build Coastguard Worker            for i in range(size):
505*cda5da8dSAndroid Build Coastguard Worker                offset += 2
506*cda5da8dSAndroid Build Coastguard Worker                # Only show the fancy argrepr for a CACHE instruction when it's
507*cda5da8dSAndroid Build Coastguard Worker                # the first entry for a particular cache value and the
508*cda5da8dSAndroid Build Coastguard Worker                # instruction using it is actually quickened:
509*cda5da8dSAndroid Build Coastguard Worker                if i == 0 and op != deop:
510*cda5da8dSAndroid Build Coastguard Worker                    data = code[offset: offset + 2 * size]
511*cda5da8dSAndroid Build Coastguard Worker                    argrepr = f"{name}: {int.from_bytes(data, sys.byteorder)}"
512*cda5da8dSAndroid Build Coastguard Worker                else:
513*cda5da8dSAndroid Build Coastguard Worker                    argrepr = ""
514*cda5da8dSAndroid Build Coastguard Worker                yield Instruction(
515*cda5da8dSAndroid Build Coastguard Worker                    "CACHE", CACHE, 0, None, argrepr, offset, None, False,
516*cda5da8dSAndroid Build Coastguard Worker                    Positions(*next(co_positions, ()))
517*cda5da8dSAndroid Build Coastguard Worker                )
518*cda5da8dSAndroid Build Coastguard Worker
519*cda5da8dSAndroid Build Coastguard Workerdef disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False):
520*cda5da8dSAndroid Build Coastguard Worker    """Disassemble a code object."""
521*cda5da8dSAndroid Build Coastguard Worker    linestarts = dict(findlinestarts(co))
522*cda5da8dSAndroid Build Coastguard Worker    exception_entries = _parse_exception_table(co)
523*cda5da8dSAndroid Build Coastguard Worker    _disassemble_bytes(_get_code_array(co, adaptive),
524*cda5da8dSAndroid Build Coastguard Worker                       lasti, co._varname_from_oparg,
525*cda5da8dSAndroid Build Coastguard Worker                       co.co_names, co.co_consts, linestarts, file=file,
526*cda5da8dSAndroid Build Coastguard Worker                       exception_entries=exception_entries,
527*cda5da8dSAndroid Build Coastguard Worker                       co_positions=co.co_positions(), show_caches=show_caches)
528*cda5da8dSAndroid Build Coastguard Worker
529*cda5da8dSAndroid Build Coastguard Workerdef _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False):
530*cda5da8dSAndroid Build Coastguard Worker    disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive)
531*cda5da8dSAndroid Build Coastguard Worker    if depth is None or depth > 0:
532*cda5da8dSAndroid Build Coastguard Worker        if depth is not None:
533*cda5da8dSAndroid Build Coastguard Worker            depth = depth - 1
534*cda5da8dSAndroid Build Coastguard Worker        for x in co.co_consts:
535*cda5da8dSAndroid Build Coastguard Worker            if hasattr(x, 'co_code'):
536*cda5da8dSAndroid Build Coastguard Worker                print(file=file)
537*cda5da8dSAndroid Build Coastguard Worker                print("Disassembly of %r:" % (x,), file=file)
538*cda5da8dSAndroid Build Coastguard Worker                _disassemble_recursive(
539*cda5da8dSAndroid Build Coastguard Worker                    x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive
540*cda5da8dSAndroid Build Coastguard Worker                )
541*cda5da8dSAndroid Build Coastguard Worker
542*cda5da8dSAndroid Build Coastguard Workerdef _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
543*cda5da8dSAndroid Build Coastguard Worker                       names=None, co_consts=None, linestarts=None,
544*cda5da8dSAndroid Build Coastguard Worker                       *, file=None, line_offset=0, exception_entries=(),
545*cda5da8dSAndroid Build Coastguard Worker                       co_positions=None, show_caches=False):
546*cda5da8dSAndroid Build Coastguard Worker    # Omit the line number column entirely if we have no line number info
547*cda5da8dSAndroid Build Coastguard Worker    show_lineno = bool(linestarts)
548*cda5da8dSAndroid Build Coastguard Worker    if show_lineno:
549*cda5da8dSAndroid Build Coastguard Worker        maxlineno = max(linestarts.values()) + line_offset
550*cda5da8dSAndroid Build Coastguard Worker        if maxlineno >= 1000:
551*cda5da8dSAndroid Build Coastguard Worker            lineno_width = len(str(maxlineno))
552*cda5da8dSAndroid Build Coastguard Worker        else:
553*cda5da8dSAndroid Build Coastguard Worker            lineno_width = 3
554*cda5da8dSAndroid Build Coastguard Worker    else:
555*cda5da8dSAndroid Build Coastguard Worker        lineno_width = 0
556*cda5da8dSAndroid Build Coastguard Worker    maxoffset = len(code) - 2
557*cda5da8dSAndroid Build Coastguard Worker    if maxoffset >= 10000:
558*cda5da8dSAndroid Build Coastguard Worker        offset_width = len(str(maxoffset))
559*cda5da8dSAndroid Build Coastguard Worker    else:
560*cda5da8dSAndroid Build Coastguard Worker        offset_width = 4
561*cda5da8dSAndroid Build Coastguard Worker    for instr in _get_instructions_bytes(code, varname_from_oparg, names,
562*cda5da8dSAndroid Build Coastguard Worker                                         co_consts, linestarts,
563*cda5da8dSAndroid Build Coastguard Worker                                         line_offset=line_offset,
564*cda5da8dSAndroid Build Coastguard Worker                                         exception_entries=exception_entries,
565*cda5da8dSAndroid Build Coastguard Worker                                         co_positions=co_positions,
566*cda5da8dSAndroid Build Coastguard Worker                                         show_caches=show_caches):
567*cda5da8dSAndroid Build Coastguard Worker        new_source_line = (show_lineno and
568*cda5da8dSAndroid Build Coastguard Worker                           instr.starts_line is not None and
569*cda5da8dSAndroid Build Coastguard Worker                           instr.offset > 0)
570*cda5da8dSAndroid Build Coastguard Worker        if new_source_line:
571*cda5da8dSAndroid Build Coastguard Worker            print(file=file)
572*cda5da8dSAndroid Build Coastguard Worker        is_current_instr = instr.offset == lasti
573*cda5da8dSAndroid Build Coastguard Worker        print(instr._disassemble(lineno_width, is_current_instr, offset_width),
574*cda5da8dSAndroid Build Coastguard Worker              file=file)
575*cda5da8dSAndroid Build Coastguard Worker    if exception_entries:
576*cda5da8dSAndroid Build Coastguard Worker        print("ExceptionTable:", file=file)
577*cda5da8dSAndroid Build Coastguard Worker        for entry in exception_entries:
578*cda5da8dSAndroid Build Coastguard Worker            lasti = " lasti" if entry.lasti else ""
579*cda5da8dSAndroid Build Coastguard Worker            end = entry.end-2
580*cda5da8dSAndroid Build Coastguard Worker            print(f"  {entry.start} to {end} -> {entry.target} [{entry.depth}]{lasti}", file=file)
581*cda5da8dSAndroid Build Coastguard Worker
582*cda5da8dSAndroid Build Coastguard Workerdef _disassemble_str(source, **kwargs):
583*cda5da8dSAndroid Build Coastguard Worker    """Compile the source string, then disassemble the code object."""
584*cda5da8dSAndroid Build Coastguard Worker    _disassemble_recursive(_try_compile(source, '<dis>'), **kwargs)
585*cda5da8dSAndroid Build Coastguard Worker
586*cda5da8dSAndroid Build Coastguard Workerdisco = disassemble                     # XXX For backwards compatibility
587*cda5da8dSAndroid Build Coastguard Worker
588*cda5da8dSAndroid Build Coastguard Worker
589*cda5da8dSAndroid Build Coastguard Worker# Rely on C `int` being 32 bits for oparg
590*cda5da8dSAndroid Build Coastguard Worker_INT_BITS = 32
591*cda5da8dSAndroid Build Coastguard Worker# Value for c int when it overflows
592*cda5da8dSAndroid Build Coastguard Worker_INT_OVERFLOW = 2 ** (_INT_BITS - 1)
593*cda5da8dSAndroid Build Coastguard Worker
594*cda5da8dSAndroid Build Coastguard Workerdef _unpack_opargs(code):
595*cda5da8dSAndroid Build Coastguard Worker    extended_arg = 0
596*cda5da8dSAndroid Build Coastguard Worker    caches = 0
597*cda5da8dSAndroid Build Coastguard Worker    for i in range(0, len(code), 2):
598*cda5da8dSAndroid Build Coastguard Worker        # Skip inline CACHE entries:
599*cda5da8dSAndroid Build Coastguard Worker        if caches:
600*cda5da8dSAndroid Build Coastguard Worker            caches -= 1
601*cda5da8dSAndroid Build Coastguard Worker            continue
602*cda5da8dSAndroid Build Coastguard Worker        op = code[i]
603*cda5da8dSAndroid Build Coastguard Worker        deop = _deoptop(op)
604*cda5da8dSAndroid Build Coastguard Worker        caches = _inline_cache_entries[deop]
605*cda5da8dSAndroid Build Coastguard Worker        if deop >= HAVE_ARGUMENT:
606*cda5da8dSAndroid Build Coastguard Worker            arg = code[i+1] | extended_arg
607*cda5da8dSAndroid Build Coastguard Worker            extended_arg = (arg << 8) if deop == EXTENDED_ARG else 0
608*cda5da8dSAndroid Build Coastguard Worker            # The oparg is stored as a signed integer
609*cda5da8dSAndroid Build Coastguard Worker            # If the value exceeds its upper limit, it will overflow and wrap
610*cda5da8dSAndroid Build Coastguard Worker            # to a negative integer
611*cda5da8dSAndroid Build Coastguard Worker            if extended_arg >= _INT_OVERFLOW:
612*cda5da8dSAndroid Build Coastguard Worker                extended_arg -= 2 * _INT_OVERFLOW
613*cda5da8dSAndroid Build Coastguard Worker        else:
614*cda5da8dSAndroid Build Coastguard Worker            arg = None
615*cda5da8dSAndroid Build Coastguard Worker            extended_arg = 0
616*cda5da8dSAndroid Build Coastguard Worker        yield (i, op, arg)
617*cda5da8dSAndroid Build Coastguard Worker
618*cda5da8dSAndroid Build Coastguard Workerdef findlabels(code):
619*cda5da8dSAndroid Build Coastguard Worker    """Detect all offsets in a byte code which are jump targets.
620*cda5da8dSAndroid Build Coastguard Worker
621*cda5da8dSAndroid Build Coastguard Worker    Return the list of offsets.
622*cda5da8dSAndroid Build Coastguard Worker
623*cda5da8dSAndroid Build Coastguard Worker    """
624*cda5da8dSAndroid Build Coastguard Worker    labels = []
625*cda5da8dSAndroid Build Coastguard Worker    for offset, op, arg in _unpack_opargs(code):
626*cda5da8dSAndroid Build Coastguard Worker        if arg is not None:
627*cda5da8dSAndroid Build Coastguard Worker            if op in hasjrel:
628*cda5da8dSAndroid Build Coastguard Worker                if _is_backward_jump(op):
629*cda5da8dSAndroid Build Coastguard Worker                    arg = -arg
630*cda5da8dSAndroid Build Coastguard Worker                label = offset + 2 + arg*2
631*cda5da8dSAndroid Build Coastguard Worker            elif op in hasjabs:
632*cda5da8dSAndroid Build Coastguard Worker                label = arg*2
633*cda5da8dSAndroid Build Coastguard Worker            else:
634*cda5da8dSAndroid Build Coastguard Worker                continue
635*cda5da8dSAndroid Build Coastguard Worker            if label not in labels:
636*cda5da8dSAndroid Build Coastguard Worker                labels.append(label)
637*cda5da8dSAndroid Build Coastguard Worker    return labels
638*cda5da8dSAndroid Build Coastguard Worker
639*cda5da8dSAndroid Build Coastguard Workerdef findlinestarts(code):
640*cda5da8dSAndroid Build Coastguard Worker    """Find the offsets in a byte code which are start of lines in the source.
641*cda5da8dSAndroid Build Coastguard Worker
642*cda5da8dSAndroid Build Coastguard Worker    Generate pairs (offset, lineno)
643*cda5da8dSAndroid Build Coastguard Worker    """
644*cda5da8dSAndroid Build Coastguard Worker    lastline = None
645*cda5da8dSAndroid Build Coastguard Worker    for start, end, line in code.co_lines():
646*cda5da8dSAndroid Build Coastguard Worker        if line is not None and line != lastline:
647*cda5da8dSAndroid Build Coastguard Worker            lastline = line
648*cda5da8dSAndroid Build Coastguard Worker            yield start, line
649*cda5da8dSAndroid Build Coastguard Worker    return
650*cda5da8dSAndroid Build Coastguard Worker
651*cda5da8dSAndroid Build Coastguard Workerdef _find_imports(co):
652*cda5da8dSAndroid Build Coastguard Worker    """Find import statements in the code
653*cda5da8dSAndroid Build Coastguard Worker
654*cda5da8dSAndroid Build Coastguard Worker    Generate triplets (name, level, fromlist) where
655*cda5da8dSAndroid Build Coastguard Worker    name is the imported module and level, fromlist are
656*cda5da8dSAndroid Build Coastguard Worker    the corresponding args to __import__.
657*cda5da8dSAndroid Build Coastguard Worker    """
658*cda5da8dSAndroid Build Coastguard Worker    IMPORT_NAME = opmap['IMPORT_NAME']
659*cda5da8dSAndroid Build Coastguard Worker    LOAD_CONST = opmap['LOAD_CONST']
660*cda5da8dSAndroid Build Coastguard Worker
661*cda5da8dSAndroid Build Coastguard Worker    consts = co.co_consts
662*cda5da8dSAndroid Build Coastguard Worker    names = co.co_names
663*cda5da8dSAndroid Build Coastguard Worker    opargs = [(op, arg) for _, op, arg in _unpack_opargs(co.co_code)
664*cda5da8dSAndroid Build Coastguard Worker                  if op != EXTENDED_ARG]
665*cda5da8dSAndroid Build Coastguard Worker    for i, (op, oparg) in enumerate(opargs):
666*cda5da8dSAndroid Build Coastguard Worker        if op == IMPORT_NAME and i >= 2:
667*cda5da8dSAndroid Build Coastguard Worker            from_op = opargs[i-1]
668*cda5da8dSAndroid Build Coastguard Worker            level_op = opargs[i-2]
669*cda5da8dSAndroid Build Coastguard Worker            if (from_op[0] in hasconst and level_op[0] in hasconst):
670*cda5da8dSAndroid Build Coastguard Worker                level = _get_const_value(level_op[0], level_op[1], consts)
671*cda5da8dSAndroid Build Coastguard Worker                fromlist = _get_const_value(from_op[0], from_op[1], consts)
672*cda5da8dSAndroid Build Coastguard Worker                yield (names[oparg], level, fromlist)
673*cda5da8dSAndroid Build Coastguard Worker
674*cda5da8dSAndroid Build Coastguard Workerdef _find_store_names(co):
675*cda5da8dSAndroid Build Coastguard Worker    """Find names of variables which are written in the code
676*cda5da8dSAndroid Build Coastguard Worker
677*cda5da8dSAndroid Build Coastguard Worker    Generate sequence of strings
678*cda5da8dSAndroid Build Coastguard Worker    """
679*cda5da8dSAndroid Build Coastguard Worker    STORE_OPS = {
680*cda5da8dSAndroid Build Coastguard Worker        opmap['STORE_NAME'],
681*cda5da8dSAndroid Build Coastguard Worker        opmap['STORE_GLOBAL']
682*cda5da8dSAndroid Build Coastguard Worker    }
683*cda5da8dSAndroid Build Coastguard Worker
684*cda5da8dSAndroid Build Coastguard Worker    names = co.co_names
685*cda5da8dSAndroid Build Coastguard Worker    for _, op, arg in _unpack_opargs(co.co_code):
686*cda5da8dSAndroid Build Coastguard Worker        if op in STORE_OPS:
687*cda5da8dSAndroid Build Coastguard Worker            yield names[arg]
688*cda5da8dSAndroid Build Coastguard Worker
689*cda5da8dSAndroid Build Coastguard Worker
690*cda5da8dSAndroid Build Coastguard Workerclass Bytecode:
691*cda5da8dSAndroid Build Coastguard Worker    """The bytecode operations of a piece of code
692*cda5da8dSAndroid Build Coastguard Worker
693*cda5da8dSAndroid Build Coastguard Worker    Instantiate this with a function, method, other compiled object, string of
694*cda5da8dSAndroid Build Coastguard Worker    code, or a code object (as returned by compile()).
695*cda5da8dSAndroid Build Coastguard Worker
696*cda5da8dSAndroid Build Coastguard Worker    Iterating over this yields the bytecode operations as Instruction instances.
697*cda5da8dSAndroid Build Coastguard Worker    """
698*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False):
699*cda5da8dSAndroid Build Coastguard Worker        self.codeobj = co = _get_code_object(x)
700*cda5da8dSAndroid Build Coastguard Worker        if first_line is None:
701*cda5da8dSAndroid Build Coastguard Worker            self.first_line = co.co_firstlineno
702*cda5da8dSAndroid Build Coastguard Worker            self._line_offset = 0
703*cda5da8dSAndroid Build Coastguard Worker        else:
704*cda5da8dSAndroid Build Coastguard Worker            self.first_line = first_line
705*cda5da8dSAndroid Build Coastguard Worker            self._line_offset = first_line - co.co_firstlineno
706*cda5da8dSAndroid Build Coastguard Worker        self._linestarts = dict(findlinestarts(co))
707*cda5da8dSAndroid Build Coastguard Worker        self._original_object = x
708*cda5da8dSAndroid Build Coastguard Worker        self.current_offset = current_offset
709*cda5da8dSAndroid Build Coastguard Worker        self.exception_entries = _parse_exception_table(co)
710*cda5da8dSAndroid Build Coastguard Worker        self.show_caches = show_caches
711*cda5da8dSAndroid Build Coastguard Worker        self.adaptive = adaptive
712*cda5da8dSAndroid Build Coastguard Worker
713*cda5da8dSAndroid Build Coastguard Worker    def __iter__(self):
714*cda5da8dSAndroid Build Coastguard Worker        co = self.codeobj
715*cda5da8dSAndroid Build Coastguard Worker        return _get_instructions_bytes(_get_code_array(co, self.adaptive),
716*cda5da8dSAndroid Build Coastguard Worker                                       co._varname_from_oparg,
717*cda5da8dSAndroid Build Coastguard Worker                                       co.co_names, co.co_consts,
718*cda5da8dSAndroid Build Coastguard Worker                                       self._linestarts,
719*cda5da8dSAndroid Build Coastguard Worker                                       line_offset=self._line_offset,
720*cda5da8dSAndroid Build Coastguard Worker                                       exception_entries=self.exception_entries,
721*cda5da8dSAndroid Build Coastguard Worker                                       co_positions=co.co_positions(),
722*cda5da8dSAndroid Build Coastguard Worker                                       show_caches=self.show_caches)
723*cda5da8dSAndroid Build Coastguard Worker
724*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
725*cda5da8dSAndroid Build Coastguard Worker        return "{}({!r})".format(self.__class__.__name__,
726*cda5da8dSAndroid Build Coastguard Worker                                 self._original_object)
727*cda5da8dSAndroid Build Coastguard Worker
728*cda5da8dSAndroid Build Coastguard Worker    @classmethod
729*cda5da8dSAndroid Build Coastguard Worker    def from_traceback(cls, tb, *, show_caches=False, adaptive=False):
730*cda5da8dSAndroid Build Coastguard Worker        """ Construct a Bytecode from the given traceback """
731*cda5da8dSAndroid Build Coastguard Worker        while tb.tb_next:
732*cda5da8dSAndroid Build Coastguard Worker            tb = tb.tb_next
733*cda5da8dSAndroid Build Coastguard Worker        return cls(
734*cda5da8dSAndroid Build Coastguard Worker            tb.tb_frame.f_code, current_offset=tb.tb_lasti, show_caches=show_caches, adaptive=adaptive
735*cda5da8dSAndroid Build Coastguard Worker        )
736*cda5da8dSAndroid Build Coastguard Worker
737*cda5da8dSAndroid Build Coastguard Worker    def info(self):
738*cda5da8dSAndroid Build Coastguard Worker        """Return formatted information about the code object."""
739*cda5da8dSAndroid Build Coastguard Worker        return _format_code_info(self.codeobj)
740*cda5da8dSAndroid Build Coastguard Worker
741*cda5da8dSAndroid Build Coastguard Worker    def dis(self):
742*cda5da8dSAndroid Build Coastguard Worker        """Return a formatted view of the bytecode operations."""
743*cda5da8dSAndroid Build Coastguard Worker        co = self.codeobj
744*cda5da8dSAndroid Build Coastguard Worker        if self.current_offset is not None:
745*cda5da8dSAndroid Build Coastguard Worker            offset = self.current_offset
746*cda5da8dSAndroid Build Coastguard Worker        else:
747*cda5da8dSAndroid Build Coastguard Worker            offset = -1
748*cda5da8dSAndroid Build Coastguard Worker        with io.StringIO() as output:
749*cda5da8dSAndroid Build Coastguard Worker            _disassemble_bytes(_get_code_array(co, self.adaptive),
750*cda5da8dSAndroid Build Coastguard Worker                               varname_from_oparg=co._varname_from_oparg,
751*cda5da8dSAndroid Build Coastguard Worker                               names=co.co_names, co_consts=co.co_consts,
752*cda5da8dSAndroid Build Coastguard Worker                               linestarts=self._linestarts,
753*cda5da8dSAndroid Build Coastguard Worker                               line_offset=self._line_offset,
754*cda5da8dSAndroid Build Coastguard Worker                               file=output,
755*cda5da8dSAndroid Build Coastguard Worker                               lasti=offset,
756*cda5da8dSAndroid Build Coastguard Worker                               exception_entries=self.exception_entries,
757*cda5da8dSAndroid Build Coastguard Worker                               co_positions=co.co_positions(),
758*cda5da8dSAndroid Build Coastguard Worker                               show_caches=self.show_caches)
759*cda5da8dSAndroid Build Coastguard Worker            return output.getvalue()
760*cda5da8dSAndroid Build Coastguard Worker
761*cda5da8dSAndroid Build Coastguard Worker
762*cda5da8dSAndroid Build Coastguard Workerdef _test():
763*cda5da8dSAndroid Build Coastguard Worker    """Simple test program to disassemble a file."""
764*cda5da8dSAndroid Build Coastguard Worker    import argparse
765*cda5da8dSAndroid Build Coastguard Worker
766*cda5da8dSAndroid Build Coastguard Worker    parser = argparse.ArgumentParser()
767*cda5da8dSAndroid Build Coastguard Worker    parser.add_argument('infile', type=argparse.FileType('rb'), nargs='?', default='-')
768*cda5da8dSAndroid Build Coastguard Worker    args = parser.parse_args()
769*cda5da8dSAndroid Build Coastguard Worker    with args.infile as infile:
770*cda5da8dSAndroid Build Coastguard Worker        source = infile.read()
771*cda5da8dSAndroid Build Coastguard Worker    code = compile(source, args.infile.name, "exec")
772*cda5da8dSAndroid Build Coastguard Worker    dis(code)
773*cda5da8dSAndroid Build Coastguard Worker
774*cda5da8dSAndroid Build Coastguard Workerif __name__ == "__main__":
775*cda5da8dSAndroid Build Coastguard Worker    _test()
776