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