def disco_loop(opc, version, queue, real_out, dup_lines=False, asm_format="classic"): """Disassembles a queue of code objects. If we discover another code object which will be found in co_consts, we add the new code to the list. Note that the order of code discovery is in the order of first encountered which is not amenable for the format used by a disassembler where code objects should be defined before using them in other functions. However this is not recursive and will overall lead to less memory consumption at run time. """ while len(queue) > 0: co = queue.popleft() if co.co_name not in ("<module>", "?"): real_out.write("\n" + format_code_info(co, version) + "\n") bytecode = Bytecode(co, opc, dup_lines=dup_lines) real_out.write(bytecode.dis(asm_format=asm_format) + "\n") for c in co.co_consts: if iscode(c): queue.append(c) pass pass
def _populate_lines(self): code = self.code last_line_info = None for instr in get_instructions_bytes( bytecode=code.co_code, opc=self.opc, varnames=code.co_varnames, names=code.co_names, constants=code.co_consts, cells=code.co_cellvars + code.co_freevars, linestarts=self.linestarts, ): offset = instr.offset self.offsets.append(offset) self.instructions.append(instr) if instr.starts_line: if last_line_info: self.lines.append(last_line_info) pass last_line_info = LineOffsets(instr.starts_line, [offset], code) else: last_line_info.offsets.append(offset) pass pass self.lines.append(last_line_info) if self.include_children: for c in code.co_consts: if iscode(c): code_info = LineOffsetInfo(self.opc, c, True) self.children[code_info.name] = code_info self.lines += code_info.lines pass pass pass
def disco( bytecode_version, co, timestamp, out=sys.stdout, is_pypy=False, magic_int=None, source_size=None, sip_hash=None, asm_format="classic", show_bytes=False, dup_lines=False, alternate_opmap=None, ): """ diassembles and deparses a given code block 'co' """ assert iscode(co) show_module_header( bytecode_version, co, timestamp, out, is_pypy, magic_int, source_size, sip_hash, header=True, show_filename=False, ) # store final output stream for case of error real_out = out or sys.stdout if co.co_filename and asm_format != "xasm": real_out.write(format_code_info(co, bytecode_version) + "\n") pass opc = get_opcode(bytecode_version, is_pypy, alternate_opmap) if asm_format == "xasm": disco_loop_asm_format(opc, bytecode_version, co, real_out, {}, set([])) else: queue = deque([co]) disco_loop(opc, bytecode_version, queue, real_out, asm_format=asm_format, dup_lines=True)
def __init__(self, opc, code, include_children=False): if not iscode(code): raise TypeError( "code parameter %s needs to be a code type; is %s" % (code, type(code)) ) self.code = code self.name = code.co_name self.opc = opc self.children = {} self.lines = [] self.offsets = [] self.linestarts = dict(opc.findlinestarts(code, dup_lines=True)) self.instructions = [] self.include_children = include_children self._populate_lines() return
def disco_loop_asm_format(opc, version, co, real_out, fn_name_map, all_fns): """Produces disassembly in a format more conducive to automatic assembly by producing inner modules before they are used by outer ones. Since this is recusive, we'll use more stack space at runtime. """ co = codeType2Portable(co) co_name = co.co_name mapped_name = fn_name_map.get(co_name, co_name) new_consts = [] for c in co.co_consts: if iscode(c): if isinstance(c, types.CodeType): c_compat = codeType2Portable(c) else: c_compat = c disco_loop_asm_format(opc, version, c_compat, real_out, fn_name_map, all_fns) m = re.match(".* object <(.+)> at", str(c)) if m: basename = m.group(1) if basename != "module": mapped_name = code_uniquify(basename, c.co_code) c_compat.co_name = mapped_name c_compat.freeze() new_consts.append(c_compat) else: new_consts.append(c) pass co.co_consts = new_consts m = re.match("^<(.+)>$", co.co_name) if m or co_name in all_fns: if co_name in all_fns: basename = co_name else: basename = m.group(1) if basename != "module": mapped_name = code_uniquify(basename, co.co_code) co_name = mapped_name assert mapped_name not in fn_name_map fn_name_map[mapped_name] = basename co.co_name = mapped_name pass elif co_name in fn_name_map: # FIXME: better would be a hash of the co_code mapped_name = code_uniquify(co_name, co.co_code) fn_name_map[mapped_name] = co_name co.co_name = mapped_name pass co = co.freeze() all_fns.add(co_name) if co.co_name != "<module>" or co.co_filename: real_out.write("\n" + format_code_info(co, version, mapped_name) + "\n") bytecode = Bytecode(co, opc, dup_lines=True) real_out.write(bytecode.dis(asm_format="asm") + "\n")