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