def test_opcode(): opc = get_opcode(PYTHON_VERSION, IS_PYPY) opmap = dict([(k.replace('+', '_'), v) for (k, v) in dis.opmap.items()]) print("Extra in dis:", set(opmap.items()) - set(opc.opmap.items())) print("Extra in xdis:", set(opc.opmap.items()) - set(opmap.items())) for item in opmap.items(): assert item in opc.opmap.items(), item fields_str = "hascompare hasconst hasfree hasjabs hasjrel haslocal" # PyPy 2.7.13 changes opcodes mid-version. It is too complicated # to figure out where the change actually occurred # Pypy 3.6.9 may or may not have JUMP_IF_NOT_DEBUG if not (IS_PYPY and PYTHON_VERSION in (2.7, 3.6)): assert all(item in opmap.items() for item in opc.opmap.items()) elif (IS_PYPY and PYTHON_VERSION == 3.6): # Don't count JUMP_IF_NOT_DEBUG mismatch fields_str = "hascompare hasconst hasfree hasjabs haslocal" assert all(item in opc.opmap.items() for item in opmap.items()) fields = fields_str.split() for field in fields: opc_set = set(getattr(opc, field)) dis_set = set(getattr(dis, field)) assert opc_set == dis_set, \ ("diff in %s: %s" % (field, ', '.join([opc.opname[i] for i in list(opc_set ^ dis_set)])))
def __init__(self, python_version, is_pypy): self.opc = get_opcode(python_version, is_pypy) self.code_list = [] self.codes = [] # FIXME use a better name self.status = "unfinished" self.size = 0 # Size of source code. Only relevant in version 3 and above self.python_version = python_version self.timestamp = None self.backpatch = [] # list of backpatch dicts, one for each function self.label = [] # list of label dists, one for each function self.code = None self.siphash = None
def test_stack_effect_fixed(): """Check stack effect of opcodes that don't vary in the stack effect. This we get from tables that are derived the Python Interpreter C source. Thanks to the Maynard project for this idea. """ versions = ((2, 5), (2, 6), (2, 7), (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5), (3, 6), (3, 7), (3, 8), (3, 9)) for version in versions: v_str = "%s%s" % (version[0], version[1]) opc = get_opcode(version, False) se_file_py = osp.realpath( osp.join(srcdir, "stackeffect", "se%s.py" % v_str)) opcode_stack_effect = eval(open(se_file_py).read()) assert len(opcode_stack_effect) == 256 for opcode in range(256): check_effect = opcode_stack_effect[opcode] if check_effect == -100: continue opname = opc.opname[opcode] # FIXME: not sure what's up with these in 2.6 if opname.startswith("SLICE"): continue if op_has_argument(opcode, opc): effect = xstack_effect(opcode, opc, 10) else: effect = xstack_effect(opcode, opc) pass for opname, opcode, in opc.opmap.items(): if op_has_argument(opcode, opc): continue # These are are not in the C code although they are documented as opcodes elif opname in ("STOP_CODE", "NOP"): continue # FIXME: not sure what's up with these in 2.6 elif opname.startswith("SLICE"): continue effect = xstack_effect(opcode, opc) check_effect = opcode_stack_effect[opcode] assert check_effect == effect, ( "in version %s %d (%s) not okay; effect xstack_effect is %d; C source has %d" % (opc.version, opcode, opname, effect, check_effect)) # print("version %s: %d (%s) is good: effect %d" % (version, opcode, opname, effect)) pass pass return
def line_number_mapping(pyc_filename, src_filename): ( version, timestamp, magic_int, code1, is_pypy, source_size, sip_hash, ) = load_module(pyc_filename) try: code2 = load_file(src_filename) except SyntaxError as e: return str(e) queue = deque([code1, code2]) mappings = [] opc = get_opcode(version, is_pypy) number_loop(queue, mappings, opc) return sorted(mappings, key=lambda x: x[1])
# std import os import difflib import subprocess import tempfile import functools # uncompyle6 / xdis from uncompyle6 import code_deparse from xdis.version_info import PYTHON_VERSION_TRIPLE, PYTHON3, IS_PYPY # TODO : I think we can get xdis to support the dis api (python 3 version) by doing something like this there from xdis import Bytecode, get_opcode opc = get_opcode(PYTHON_VERSION_TRIPLE, IS_PYPY) Bytecode = functools.partial(Bytecode, opc=opc) import six if PYTHON3: from io import StringIO else: from StringIO import StringIO def _dis_to_text(co): return Bytecode(co).dis() def print_diff(original, uncompyled): """
# std import os import difflib import subprocess import tempfile import functools # decompyle3 / xdis from decompyle3 import PYTHON_VERSION, IS_PYPY, code_deparse # TODO : I think we can get xdis to support the dis api (python 3 version) by doing something like this there from xdis import Bytecode, get_opcode opc = get_opcode(PYTHON_VERSION, IS_PYPY) Bytecode = functools.partial(Bytecode, opc=opc) import six def _dis_to_text(co): return Bytecode(co).dis() def print_diff(original, uncompyled): """ Try and display a pretty html line difference between the original and uncompyled code and bytecode if elinks and BeautifulSoup are installed otherwise just show the diff. :param original: Text describing the original code object. :param uncompyled: Text describing the uncompyled code object. """
def dis( msg, msg_nocr, section, errmsg, x=None, start_line=-1, end_line=None, relative_pos=False, highlight="light", start_offset=0, end_offset=None, include_header=False, asm_format="extended", ): """Disassemble classes, methods, functions, or code. With no argument, disassemble the last traceback. """ lasti = -1 if x is None: distb() return None, None if start_offset is None: start_offset = 0 mess = "" if start_line > 1: mess += "from line %d " % start_line elif start_offset > 1: mess = "from offset %d " % start_offset if end_line: mess += "to line %d" % end_line elif end_offset: mess += "to offset %d" % end_offset sectioned = False # Try to dogpaddle to the code object for the type setting x if hasattr(types, "InstanceType") and isinstance(x, types.InstanceType): x = x.__class__ if inspect.ismethod(x): section("Disassembly of %s: %s" % (x, mess)) sectioned = True x = x.__func__.__code__ elif inspect.isfunction(x) or inspect.isgeneratorfunction(x): section("Disassembly of %s: %s" % (x, mess)) x = x.__code__ sectioned = True elif inspect.isgenerator(x): section("Disassembly of %s: %s" % (x, mess)) frame = x.gi_frame lasti = frame.f_last_i x = x.gi_code sectioned = True elif inspect.isframe(x): section("Disassembly of %s: %s" % (x, mess)) sectioned = True if hasattr(x, "f_lasti"): lasti = x.f_lasti if lasti == -1: lasti = 0 pass opc = get_opcode(PYTHON_VERSION_TRIPLE, IS_PYPY) x = x.f_code if include_header: header_lines = Bytecode(x, opc).info().split("\n") header = "\n".join([format_token(Comment, h) for h in header_lines]) msg(header) pass elif inspect.iscode(x): pass if hasattr(x, "__dict__"): # Class or module items = sorted(x.__dict__.items()) for name, x1 in items: if isinstance(x1, _have_code): if not sectioned: section("Disassembly of %s: " % x) try: dis( msg, msg_nocr, section, errmsg, x1, start_line=start_line, end_line=end_line, relative_pos=relative_pos, asm_format=asm_format, ) msg("") except TypeError: _, msg, _ = sys.exc_info() errmsg("Sorry:", msg) pass pass pass pass elif hasattr(x, "co_code"): # Code object if not sectioned: section("Disassembly of %s: " % x) return disassemble( msg, msg_nocr, section, x, lasti=lasti, start_line=start_line, end_line=end_line, relative_pos=relative_pos, highlight=highlight, start_offset=start_offset, end_offset=end_offset, asm_format=asm_format, ) elif isinstance(x, str): # Source code return disassemble_string( msg, msg_nocr, x, ) else: errmsg("Don't know how to disassemble %s objects." % type(x).__name__) return None, None
magic_int, code1, is_pypy, source_size, sip_hash, ) = load_module(pyc_filename) try: code2 = load_file(src_filename) except SyntaxError, e: return str(e) queue = deque([code1, code2]) mappings = [] opc = get_opcode(version, is_pypy) number_loop(queue, mappings, opc) return sorted(mappings, key=lambda x: x[1]) def number_loop(queue, mappings, opc): while len(queue) > 0: code1 = queue.popleft() code2 = queue.popleft() assert code1.co_name == code2.co_name linestarts_orig = findlinestarts(code1) linestarts_uncompiled = list(findlinestarts(code2)) mappings += [[line, offset2line(offset, linestarts_uncompiled)] for offset, line in linestarts_orig] bytecode1 = Bytecode(code1, opc) bytecode2 = Bytecode(code2, opc)
#!/usr/bin/env python """ This is a translation into Python of the seXY.c programs. However this can only used in Python 3.4 and above which has dis.stack_effect(). """ from xdis import PYTHON_VERSION import dis NOTFIXED = -100 from xdis.cross_dis import op_has_argument from xdis import get_opcode print("# Python %s Stack effects\n" % PYTHON_VERSION) assert PYTHON_VERSION >= 3.4, "This only works for Python version 3.4 and above; you have version %s." % PYTHON_VERSION print("[") opc = get_opcode(PYTHON_VERSION, False) for i in range(256): try: if op_has_argument(i, opc): effect = dis.stack_effect(i, 0) opargs_to_try = [ -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 256, 1000, 0xffff, 0 ] for operand in opargs_to_try: with_oparg = dis.stack_effect(i, operand) if effect != with_oparg: effect = NOTFIXED break pass else: effect = dis.stack_effect(i) pass