disassembler of python byte code into mnemonics import sys import types import collections import io from opcode import from opcode import all as opcodesall cacheformat inlinecacheentries nbops intrinsic1descs intrinsic2descs specializations specializedopmap all codeinfo dis disassemble distb disco findlinestarts findlabels showcode getinstructions instruction bytecode opcodesall del opcodesall havecode types methodtype types functiontype types codetype classmethod staticmethod type convertvalue opmap convertvalue setfunctionattribute opmap setfunctionattribute functionattrflags defaults kwdefaults annotations closure enterexecutor opmap enterexecutor loadconst opmap loadconst returnconst opmap returnconst loadglobal opmap loadglobal binaryop opmap binaryop jumpbackward opmap jumpbackward foriter opmap foriter send opmap send loadattr opmap loadattr loadsuperattr opmap loadsuperattr callintrinsic1 opmap callintrinsic1 callintrinsic2 opmap callintrinsic2 loadfastloadfast opmap loadfastloadfast storefastloadfast opmap storefastloadfast storefaststorefast opmap storefaststorefast cache opmapcache allopname listopname allopmap dictopmap for name op in specializedopmap items fill opname and opmap assert op lenallopname allopnameop name allopmapname op deoptmap specialized base for base family in specializations items for specialized in family def trycompilesource name try return compilesource name eval except syntaxerror pass return compilesource name exec def disxnone filenone depthnone showcachesfalse adaptivefalse showoffsetsfalse if x is none distbfilefile showcachesshowcaches adaptiveadaptive showoffsetsshowoffsets return extract functions from methods if hasattrx func x x func extract compiled code objects from if hasattrx code a function or x x code elif hasattrx gicode a generator object or x x gicode elif hasattrx agcode an asynchronous generator object or x x agcode elif hasattrx crcode a coroutine x x crcode perform the disassembly if hasattrx dict class or module items sortedx dict items for name x1 in items if isinstancex1 havecode printdisassembly of s name filefile try disx1 filefile depthdepth showcachesshowcaches adaptiveadaptive showoffsetsshowoffsets except typeerror as msg printsorry msg filefile printfilefile elif hasattrx cocode code object disassemblerecursivex filefile depthdepth showcachesshowcaches adaptiveadaptive showoffsetsshowoffsets elif isinstancex bytes bytearray raw bytecode disassemblebytesx filefile showcachesshowcaches showoffsetsshowoffsets elif isinstancex str source code disassemblestrx filefile depthdepth showcachesshowcaches adaptiveadaptive showoffsetsshowoffsets else raise typeerrordon t know how to disassemble s objects typex name def distbtbnone filenone showcachesfalse adaptivefalse showoffsetsfalse the inspect module interrogates this dictionary to build its list of co constants it is also used by prettyflags to turn the coflags field into a human readable list return pretty representation of code flags names for i in range32 flag 1i if flags flag names appendcompilerflagnames getflag hexflag flags flag if not flags break else names appendhexflags return joinnames class unknown def reprself return unknown sentinel to represent values that cannot be calculated unknown unknown def getcodeobjectx extract functions from methods extract compiled code objects from handle source code by now if we don t have a code object we can t disassemble x formatted details of methods functions or code return formatcodeinfogetcodeobjectx def formatcodeinfoco lines lines appendname s co coname lines appendfilename s co cofilename lines appendargument count s co coargcount lines appendpositionalonly arguments s co coposonlyargcount lines appendkwonly arguments s co cokwonlyargcount lines appendnumber of locals s co conlocals lines appendstack size s co costacksize lines appendflags s prettyflagsco coflags if co coconsts lines appendconstants for ic in enumerateco coconsts lines append4d r ic if co conames lines appendnames for in in enumerateco conames lines append4d s in if co covarnames lines appendvariable names for in in enumerateco covarnames lines append4d s in if co cofreevars lines appendfree variables for in in enumerateco cofreevars lines append4d s in if co cocellvars lines appendcell variables for in in enumerateco cocellvars lines append4d s in return n joinlines def showcodeco filenone printcodeinfoco filefile positions collections namedtuple positions lineno endlineno coloffset endcoloffset defaultsnone 4 instruction collections namedtuple instruction opname opcode arg argval argrepr offset startoffset startsline linenumber label positions defaultsnone none instruction opname doc human readable name for operation instruction opcode doc numeric code for operation instruction arg doc numeric argument to operation if any otherwise none instruction argval doc resolved arg value if known otherwise same as arg instruction argrepr doc human readable description of operation argument instruction offset doc start index of operation within bytecode sequence instruction startoffset doc start index of operation within bytecode sequence including extended args if present otherwise equal to instruction offset instruction startsline doc true if this opcode starts a source line otherwise false instruction linenumber doc source line number associated with this opcode if any otherwise none instruction label doc a label int 0 if this instruction is a jump target otherwise none instruction positions doc dis positions object holding the span of source code covered by this instruction exceptiontableentrybase collections namedtupleexceptiontableentrybase start end target depth lasti class exceptiontableentryexceptiontableentrybase pass opnamewidth 20 opargwidth 5 def getcachesizeopname return inlinecacheentries getopname 0 def getjumptargetop arg offset deop deoptopop caches getcachesizeallopnamedeop if deop in hasjrel if isbackwardjumpdeop arg arg target offset 2 arg2 target 2 caches elif deop in hasjabs target arg2 else target none return target class instructioninstruction staticmethod def getargvalargreprop arg offset coconsts names varnamefromoparg labelsmap getname none if names is none else names getitem argval none argrepr deop deoptopop if arg is not none set argval to the dereferenced value of the argument when available and argrepr to the string representation of argval disassemblebytes needs the string repr of the raw name index for loadglobal loadconst etc argval arg if deop in hasconst argval argrepr getconstinfodeop arg coconsts elif deop in hasname if deop loadglobal argval argrepr getnameinfoarg2 getname if arg 1 and argrepr argrepr fargrepr null elif deop loadattr argval argrepr getnameinfoarg2 getname if arg 1 and argrepr argrepr fargrepr nullself elif deop loadsuperattr argval argrepr getnameinfoarg4 getname if arg 1 and argrepr argrepr fargrepr nullself else argval argrepr getnameinfoarg getname elif deop in hasjabs argval arg2 argrepr fto llabelsmapargval elif deop in hasjrel signedarg arg if isbackwardjumpdeop else arg argval offset 2 signedarg2 caches getcachesizeallopnamedeop argval 2 caches if deop enterexecutor argval 2 argrepr fto llabelsmapargval elif deop in loadfastloadfast storefastloadfast storefaststorefast arg1 arg 4 arg2 arg 15 val1 argrepr1 getnameinfoarg1 varnamefromoparg val2 argrepr2 getnameinfoarg2 varnamefromoparg argrepr argrepr1 argrepr2 argval val1 val2 elif deop in haslocal or deop in hasfree argval argrepr getnameinfoarg varnamefromoparg elif deop in hascompare argval cmpoparg 5 argrepr argval if arg 16 argrepr fboolargrepr elif deop convertvalue argval none str repr asciiarg argrepr str repr ascii arg elif deop setfunctionattribute argrepr joins for i s in enumeratefunctionattrflags if arg 1i elif deop binaryop argrepr nbopsarg elif deop callintrinsic1 argrepr intrinsic1descsarg elif deop callintrinsic2 argrepr intrinsic2descsarg return argval argrepr classmethod def createcls op arg offset startoffset startsline linenumber positions coconstsnone varnamefromopargnone namesnone labelsmapnone exceptionsmapnone argval argrepr cls getargvalargrepr op arg offset coconsts names varnamefromoparg labelsmap label labelsmap getoffset none instr instructionallopnameop op arg argval argrepr offset startoffset startsline linenumber label positions instr labelwidth 4 lenstrlenlabelsmap instr exchandler exceptionsmap getoffset none return instr property def opargself numeric code for the base operation if operation is specialized otherwise equal to instruction opcode human readable name for the base operation if operation is specialized otherwise equal to instruction opname start index of the cache entries following the operation return self offset 2 property def endoffsetself bytecode index of the jump target if this is a jump operation otherwise return none true if other code jumps to here otherwise false return self label is not none def disassembleself linenowidth3 markascurrentfalse offsetwidth0 labelwidth0 fields column source code line number if linenowidth if self startsline linenofmt dd if self linenumber is not none else ds linenofmt linenofmt linenowidth lineno self linenumber if self linenumber is not none else fields appendlinenofmt lineno else fields append linenowidth column label if self label is not none lbl flself label fields appendflbl labelwidth else fields append labelwidth column instruction offset from start of code sequence if offsetwidth 0 fields appendfreprself offset offsetwidth column current instruction indicator if markascurrent fields append else fields append column opcode name fields appendself opname ljustopnamewidth column opcode argument if self arg is not none arg reprself arg if opname is longer than opnamewidth we allow it to overflow into the space reserved for oparg this results in fewer misaligned opargs in the disassembly output opnameexcess max0 lenself opname opnamewidth fields appendreprself arg rjustopargwidth opnameexcess column opcode argument details if self argrepr fields append self argrepr return joinfields rstrip def strself return self disassemble def getinstructionsx firstlinenone showcachesfalse adaptivefalse co getcodeobjectx linestarts dictfindlinestartsco if firstline is not none lineoffset firstline co cofirstlineno else lineoffset 0 return getinstructionsbytesgetcodearrayco adaptive co varnamefromoparg co conames co coconsts linestarts lineoffset copositionsco copositions showcachesshowcaches originalcodeco cocode def getconstvalueop arg coconsts assert op in hasconst argval unknown if coconsts is not none argval coconstsarg return argval def getconstinfoop arg coconsts argval getconstvalueop arg coconsts argrepr reprargval if argval is not unknown else return argval argrepr def getnameinfonameindex getname extrainfo if getname is not none argval getnamenameindex extrainfo return argval argval else return unknown def parsevarintiterator b nextiterator val b 63 while b64 val 6 b nextiterator val b63 return val def parseexceptiontablecode iterator itercode coexceptiontable entries try while true start parsevarintiterator2 length parsevarintiterator2 end start length target parsevarintiterator2 dl parsevarintiterator depth dl 1 lasti booldl1 entries appendexceptiontableentrystart end target depth lasti except stopiteration return entries def isbackwardjumpop return opnameop in jumpbackward jumpbackwardnointerrupt enterexecutor def getinstructionsbytescode varnamefromopargnone namesnone coconstsnone linestartsnone lineoffset0 exceptionentries copositionsnone showcachesfalse originalcodenone use the basic unadaptive code for finding labels and actually walking the bytecode since replacements like enterexecutor and instrumented can mess that logic up pretty badly originalcode originalcode or code copositions copositions or iter getname none if names is none else names getitem def makelabelsmaporiginalcode exceptionentries jumptargets setfindlabelsoriginalcode labels setjumptargets for start end target in exceptionentries labels addstart labels addend labels addtarget labels sortedlabels labelsmap offset i1 for i offset in enumeratesortedlabels for e in exceptionentries e startlabel labelsmape start e endlabel labelsmape end e targetlabel labelsmape target return labelsmap labelsmap makelabelsmaporiginalcode exceptionentries labelwidth 4 lenstrlenlabelsmap exceptionsmap for start end target in exceptionentries exceptionsmapstart labelsmaptarget exceptionsmapend 1 startsline false locallinenumber none linenumber none for offset startoffset op arg in unpackopargsoriginalcode if linestarts is not none startsline offset in linestarts if startsline locallinenumber linestartsoffset if locallinenumber is not none linenumber locallinenumber lineoffset else linenumber none positions positionsnextcopositions deop deoptopop op codeoffset yield instruction createop arg offset startoffset startsline linenumber positions coconstscoconsts varnamefromopargvarnamefromoparg namesnames labelsmaplabelsmap exceptionsmapexceptionsmap caches getcachesizeallopnamedeop if not caches continue if not showcaches we still need to advance the copositions iterator for in rangecaches nextcopositions continue for name size in cacheformatopnamedeop items for i in rangesize offset 2 only show the fancy argrepr for a cache instruction when it s the first entry for a particular cache value if i 0 data codeoffset offset 2 size argrepr fname int frombytesdata sys byteorder else argrepr yield instruction cache cache 0 none argrepr offset offset false none none positionsnextcopositions def disassembleco lasti1 filenone showcachesfalse adaptivefalse showoffsetsfalse omit the line number column entirely if we have no line number info each cache takes 2 bytes compile the source string then disassemble the code object disassemblerecursivetrycompilesource dis kwargs disco disassemble xxx for backwards compatibility rely on c int being 32 bits for oparg intbits 32 value for c int when it overflows intoverflow 2 intbits 1 def unpackopargscode extendedarg 0 extendedargsoffset 0 number of extendedarg instructions preceding the current instruction caches 0 for i in range0 lencode 2 skip inline cache entries if caches caches 1 continue op codei deop deoptopop caches getcachesizeallopnamedeop if deop in hasarg arg codei1 extendedarg extendedarg arg 8 if deop extendedarg else 0 the oparg is stored as a signed integer if the value exceeds its upper limit it will overflow and wrap to a negative integer if extendedarg intoverflow extendedarg 2 intoverflow else arg none extendedarg 0 if deop extendedarg extendedargsoffset 1 yield i i op arg else startoffset i extendedargsoffset2 yield i startoffset op arg extendedargsoffset 0 def findlabelscode labels for offset op arg in unpackopargscode if arg is not none label getjumptargetop arg offset if label is none continue if label not in labels labels appendlabel return labels def findlinestartscode lastline false none is a valid line number for start end line in code colines if line is not lastline lastline line yield start line return def findimportsco importname opmap importname consts co coconsts names co conames opargs op arg for op arg in unpackopargsco cocode if op extendedarg for i op oparg in enumerateopargs if op importname and i 2 fromop opargsi1 levelop opargsi2 if fromop0 in hasconst and levelop0 in hasconst level getconstvaluelevelop0 levelop1 consts fromlist getconstvaluefromop0 fromop1 consts yield namesoparg level fromlist def findstorenamesco storeops opmap storename opmap storeglobal names co conames for op arg in unpackopargsco cocode if op in storeops yield namesarg class bytecode def initself x firstlinenone currentoffsetnone showcachesfalse adaptivefalse showoffsetsfalse self codeobj co getcodeobjectx if firstline is none self firstline co cofirstlineno self lineoffset 0 else self firstline firstline self lineoffset firstline co cofirstlineno self linestarts dictfindlinestartsco self originalobject x self currentoffset currentoffset self exceptionentries parseexceptiontableco self showcaches showcaches self adaptive adaptive self showoffsets showoffsets def iterself co self codeobj return getinstructionsbytesgetcodearrayco self adaptive co varnamefromoparg co conames co coconsts self linestarts lineoffsetself lineoffset exceptionentriesself exceptionentries copositionsco copositions showcachesself showcaches originalcodeco cocode def reprself return r formatself class name self originalobject classmethod def fromtracebackcls tb showcachesfalse adaptivefalse return formatted information about the code object return formatcodeinfoself codeobj def disself fill opname and opmap attempts to compile the given source first as an expression and then as a statement if the first approach fails utility function to accept strings in functions that otherwise expect code objects disassemble classes methods functions and other compiled objects with no argument disassemble the last traceback compiled objects currently include generator objects async generator objects and coroutine objects all of which store their code object in a special attribute extract functions from methods extract compiled code objects from a function or a generator object or an asynchronous generator object or a coroutine perform the disassembly class or module code object raw bytecode source code disassemble a traceback default last traceback the inspect module interrogates this dictionary to build its list of co_ constants it is also used by pretty_flags to turn the co_flags field into a human readable list return pretty representation of code flags sentinel to represent values that cannot be calculated helper to handle methods compiled or raw code objects and strings extract functions from methods extract compiled code objects from a function or a generator object or an asynchronous generator object or a coroutine handle source code by now if we don t have a code object we can t disassemble x formatted details of methods functions or code print details of methods functions or code to file if file is not provided the output is printed on stdout gets the bytecode offset of the jump target if this is a jump instruction otherwise return none details for a bytecode operation defined fields opname human readable name for operation opcode numeric code for operation arg numeric argument to operation if any otherwise none argval resolved arg value if known otherwise same as arg argrepr human readable description of operation argument offset start index of operation within bytecode sequence start_offset start index of operation within bytecode sequence including extended args if present otherwise equal to instruction offset starts_line true if this opcode starts a source line otherwise false line_number source line number associated with this opcode if any otherwise none label a label if this instruction is a jump target otherwise none positions optional dis positions object holding the span of source code covered by this instruction set argval to the dereferenced value of the argument when available and argrepr to the string representation of argval _disassemble_bytes needs the string repr of the raw name index for load_global load_const etc alias for instruction arg numeric code for the base operation if operation is specialized otherwise equal to instruction opcode human readable name for the base operation if operation is specialized otherwise equal to instruction opname start index of the cache entries following the operation end index of the cache entries following the operation bytecode index of the jump target if this is a jump operation otherwise return none true if other code jumps to here otherwise false format instruction details for inclusion in disassembly output lineno_width sets the width of the line number field 0 omits it mark_as_current inserts a marker arrow as part of the line offset_width sets the width of the instruction offset field label_width sets the width of the label field column source code line number column label column instruction offset from start of code sequence column current instruction indicator column opcode name column opcode argument if opname is longer than _opname_width we allow it to overflow into the space reserved for oparg this results in fewer misaligned opargs in the disassembly output column opcode argument details iterator for the opcodes in methods functions or code generates a series of instruction named tuples giving the details of each operations in the supplied code if first_line is not none it indicates the line number that should be reported for the first source line in the disassembled code otherwise the source line information if any is taken directly from the disassembled code object helper to get the value of the const in a hasconst op returns the dereferenced constant if this is possible otherwise if it is a load_const and co_consts is not provided returns the dis unknown sentinel helper to get optional details about const references returns the dereferenced constant and its repr if the value can be calculated otherwise returns the sentinel value dis unknown for the value and an empty string for its repr helper to get optional details about named references returns the dereferenced name as both value and repr if the name list is defined otherwise returns the sentinel value dis unknown for the value and an empty string for its repr iterate over the instructions in a bytecode string generates a sequence of instruction namedtuples giving the details of each opcode additional information about the code s runtime environment e g variable names co_consts can be specified using optional arguments use the basic unadaptive code for finding labels and actually walking the bytecode since replacements like enter_executor and instrumented_ can mess that logic up pretty badly we still need to advance the co_positions iterator only show the fancy argrepr for a cache instruction when it s the first entry for a particular cache value disassemble a code object omit the line number column entirely if we have no line number info each cache takes 2 bytes compile the source string then disassemble the code object xxx for backwards compatibility rely on c int being 32 bits for oparg value for c int when it overflows number of extended_arg instructions preceding the current instruction skip inline cache entries the oparg is stored as a signed integer if the value exceeds its upper limit it will overflow and wrap to a negative integer detect all offsets in a byte code which are jump targets return the list of offsets find the offsets in a byte code which are start of lines in the source generate pairs offset lineno lineno will be an integer or none the offset does not have a source line none is a valid line number find import statements in the code generate triplets name level fromlist where name is the imported module and level fromlist are the corresponding args to __import__ find names of variables which are written in the code generate sequence of strings the bytecode operations of a piece of code instantiate this with a function method other compiled object string of code or a code object as returned by compile iterating over this yields the bytecode operations as instruction instances construct a bytecode from the given traceback return formatted information about the code object return a formatted view of the bytecode operations
import sys
import types
import collections
import io

from opcode import *
from opcode import (
    __all__ as _opcodes_all,
    _cache_format,
    _inline_cache_entries,
    _nb_ops,
    _intrinsic_1_descs,
    _intrinsic_2_descs,
    _specializations,
    _specialized_opmap,
)

__all__ = ["code_info", "dis", "disassemble", "distb", "disco",
           "findlinestarts", "findlabels", "show_code",
           "get_instructions", "Instruction", "Bytecode"] + _opcodes_all
del _opcodes_all

_have_code = (types.MethodType, types.FunctionType, types.CodeType,
              classmethod, staticmethod, type)

CONVERT_VALUE = opmap['CONVERT_VALUE']

SET_FUNCTION_ATTRIBUTE = opmap['SET_FUNCTION_ATTRIBUTE']
FUNCTION_ATTR_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure')

ENTER_EXECUTOR = opmap['ENTER_EXECUTOR']
LOAD_CONST = opmap['LOAD_CONST']
RETURN_CONST = opmap['RETURN_CONST']
LOAD_GLOBAL = opmap['LOAD_GLOBAL']
BINARY_OP = opmap['BINARY_OP']
JUMP_BACKWARD = opmap['JUMP_BACKWARD']
FOR_ITER = opmap['FOR_ITER']
SEND = opmap['SEND']
LOAD_ATTR = opmap['LOAD_ATTR']
LOAD_SUPER_ATTR = opmap['LOAD_SUPER_ATTR']
CALL_INTRINSIC_1 = opmap['CALL_INTRINSIC_1']
CALL_INTRINSIC_2 = opmap['CALL_INTRINSIC_2']
LOAD_FAST_LOAD_FAST = opmap['LOAD_FAST_LOAD_FAST']
STORE_FAST_LOAD_FAST = opmap['STORE_FAST_LOAD_FAST']
STORE_FAST_STORE_FAST = opmap['STORE_FAST_STORE_FAST']

CACHE = opmap["CACHE"]

_all_opname = list(opname)
_all_opmap = dict(opmap)
for name, op in _specialized_opmap.items():
    
    assert op < len(_all_opname)
    _all_opname[op] = name
    _all_opmap[name] = op

deoptmap = {
    specialized: base for base, family in _specializations.items() for specialized in family
}

def _try_compile(source, name):
    
    try:
        return compile(source, name, 'eval')
    except SyntaxError:
        pass
    return compile(source, name, 'exec')

def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
        show_offsets=False):
    
    if x is None:
        distb(file=file, show_caches=show_caches, adaptive=adaptive,
              show_offsets=show_offsets)
        return
    
    if hasattr(x, '__func__'):
        x = x.__func__
    
    if hasattr(x, '__code__'):  
        x = x.__code__
    elif hasattr(x, 'gi_code'):  
        x = x.gi_code
    elif hasattr(x, 'ag_code'):  
        x = x.ag_code
    elif hasattr(x, 'cr_code'):  
        x = x.cr_code
    
    if hasattr(x, '__dict__'):  
        items = sorted(x.__dict__.items())
        for name, x1 in items:
            if isinstance(x1, _have_code):
                print("Disassembly of %s:" % name, file=file)
                try:
                    dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets)
                except TypeError as msg:
                    print("Sorry:", msg, file=file)
                print(file=file)
    elif hasattr(x, 'co_code'): 
        _disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets)
    elif isinstance(x, (bytes, bytearray)): 
        _disassemble_bytes(x, file=file, show_caches=show_caches, show_offsets=show_offsets)
    elif isinstance(x, str):    
        _disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets)
    else:
        raise TypeError("don't know how to disassemble %s objects" %
                        type(x).__name__)

def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets=False):
    
    if tb is None:
        try:
            if hasattr(sys, 'last_exc'):
                tb = sys.last_exc.__traceback__
            else:
                tb = sys.last_traceback
        except AttributeError:
            raise RuntimeError("no last traceback to disassemble") from None
        while tb.tb_next: tb = tb.tb_next
    disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets)




COMPILER_FLAG_NAMES = {
     1: "OPTIMIZED",
     2: "NEWLOCALS",
     4: "VARARGS",
     8: "VARKEYWORDS",
    16: "NESTED",
    32: "GENERATOR",
    64: "NOFREE",
   128: "COROUTINE",
   256: "ITERABLE_COROUTINE",
   512: "ASYNC_GENERATOR",
}

def pretty_flags(flags):
    
    names = []
    for i in range(32):
        flag = 1<<i
        if flags & flag:
            names.append(COMPILER_FLAG_NAMES.get(flag, hex(flag)))
            flags ^= flag
            if not flags:
                break
    else:
        names.append(hex(flags))
    return ", ".join(names)

class _Unknown:
    def __repr__(self):
        return "<unknown>"


UNKNOWN = _Unknown()

def _get_code_object(x):
    
    
    if hasattr(x, '__func__'):
        x = x.__func__
    
    if hasattr(x, '__code__'):  
        x = x.__code__
    elif hasattr(x, 'gi_code'):  
        x = x.gi_code
    elif hasattr(x, 'ag_code'):  
        x = x.ag_code
    elif hasattr(x, 'cr_code'):  
        x = x.cr_code
    
    if isinstance(x, str):
        x = _try_compile(x, "<disassembly>")
    
    if hasattr(x, 'co_code'):
        return x
    raise TypeError("don't know how to disassemble %s objects" %
                    type(x).__name__)

def _deoptop(op):
    name = _all_opname[op]
    return _all_opmap[deoptmap[name]] if name in deoptmap else op

def _get_code_array(co, adaptive):
    return co._co_code_adaptive if adaptive else co.co_code

def code_info(x):
    
    return _format_code_info(_get_code_object(x))

def _format_code_info(co):
    lines = []
    lines.append("Name:              %s" % co.co_name)
    lines.append("Filename:          %s" % co.co_filename)
    lines.append("Argument count:    %s" % co.co_argcount)
    lines.append("Positional-only arguments: %s" % co.co_posonlyargcount)
    lines.append("Kw-only arguments: %s" % co.co_kwonlyargcount)
    lines.append("Number of locals:  %s" % co.co_nlocals)
    lines.append("Stack size:        %s" % co.co_stacksize)
    lines.append("Flags:             %s" % pretty_flags(co.co_flags))
    if co.co_consts:
        lines.append("Constants:")
        for i_c in enumerate(co.co_consts):
            lines.append("%4d: %r" % i_c)
    if co.co_names:
        lines.append("Names:")
        for i_n in enumerate(co.co_names):
            lines.append("%4d: %s" % i_n)
    if co.co_varnames:
        lines.append("Variable names:")
        for i_n in enumerate(co.co_varnames):
            lines.append("%4d: %s" % i_n)
    if co.co_freevars:
        lines.append("Free variables:")
        for i_n in enumerate(co.co_freevars):
            lines.append("%4d: %s" % i_n)
    if co.co_cellvars:
        lines.append("Cell variables:")
        for i_n in enumerate(co.co_cellvars):
            lines.append("%4d: %s" % i_n)
    return "\n".join(lines)

def show_code(co, *, file=None):
    
    print(code_info(co), file=file)

Positions = collections.namedtuple(
    'Positions',
    [
        'lineno',
        'end_lineno',
        'col_offset',
        'end_col_offset',
    ],
    defaults=[None] * 4
)

_Instruction = collections.namedtuple(
    "_Instruction",
    [
        'opname',
        'opcode',
        'arg',
        'argval',
        'argrepr',
        'offset',
        'start_offset',
        'starts_line',
        'line_number',
        'label',
        'positions'
    ],
    defaults=[None, None]
)

_Instruction.opname.__doc__ = "Human readable name for operation"
_Instruction.opcode.__doc__ = "Numeric code for operation"
_Instruction.arg.__doc__ = "Numeric argument to operation (if any), otherwise None"
_Instruction.argval.__doc__ = "Resolved arg value (if known), otherwise same as arg"
_Instruction.argrepr.__doc__ = "Human readable description of operation argument"
_Instruction.offset.__doc__ = "Start index of operation within bytecode sequence"
_Instruction.start_offset.__doc__ = (
    "Start index of operation within bytecode sequence, including extended args if present; "
    "otherwise equal to Instruction.offset"
)
_Instruction.starts_line.__doc__ = "True if this opcode starts a source line, otherwise False"
_Instruction.line_number.__doc__ = "source line number associated with this opcode (if any), otherwise None"
_Instruction.label.__doc__ = "A label (int > 0) if this instruction is a jump target, otherwise None"
_Instruction.positions.__doc__ = "dis.Positions object holding the span of source code covered by this instruction"

_ExceptionTableEntryBase = collections.namedtuple("_ExceptionTableEntryBase",
    "start end target depth lasti")

class _ExceptionTableEntry(_ExceptionTableEntryBase):
    pass

_OPNAME_WIDTH = 20
_OPARG_WIDTH = 5

def _get_cache_size(opname):
    return _inline_cache_entries.get(opname, 0)

def _get_jump_target(op, arg, offset):
    
    deop = _deoptop(op)
    caches = _get_cache_size(_all_opname[deop])
    if deop in hasjrel:
        if _is_backward_jump(deop):
            arg = -arg
        target = offset + 2 + arg*2
        target += 2 * caches
    elif deop in hasjabs:
        target = arg*2
    else:
        target = None
    return target

class Instruction(_Instruction):
    

    @staticmethod
    def _get_argval_argrepr(op, arg, offset, co_consts, names, varname_from_oparg,
                            labels_map):
        get_name = None if names is None else names.__getitem__
        argval = None
        argrepr = ''
        deop = _deoptop(op)
        if arg is not None:
            
            
            
            
            argval = arg
            if deop in hasconst:
                argval, argrepr = _get_const_info(deop, arg, co_consts)
            elif deop in hasname:
                if deop == LOAD_GLOBAL:
                    argval, argrepr = _get_name_info(arg//2, get_name)
                    if (arg & 1) and argrepr:
                        argrepr = f"{argrepr} + NULL"
                elif deop == LOAD_ATTR:
                    argval, argrepr = _get_name_info(arg//2, get_name)
                    if (arg & 1) and argrepr:
                        argrepr = f"{argrepr} + NULL|self"
                elif deop == LOAD_SUPER_ATTR:
                    argval, argrepr = _get_name_info(arg//4, get_name)
                    if (arg & 1) and argrepr:
                        argrepr = f"{argrepr} + NULL|self"
                else:
                    argval, argrepr = _get_name_info(arg, get_name)
            elif deop in hasjabs:
                argval = arg*2
                argrepr = f"to L{labels_map[argval]}"
            elif deop in hasjrel:
                signed_arg = -arg if _is_backward_jump(deop) else arg
                argval = offset + 2 + signed_arg*2
                caches = _get_cache_size(_all_opname[deop])
                argval += 2 * caches
                if deop == ENTER_EXECUTOR:
                    argval += 2
                argrepr = f"to L{labels_map[argval]}"
            elif deop in (LOAD_FAST_LOAD_FAST, STORE_FAST_LOAD_FAST, STORE_FAST_STORE_FAST):
                arg1 = arg >> 4
                arg2 = arg & 15
                val1, argrepr1 = _get_name_info(arg1, varname_from_oparg)
                val2, argrepr2 = _get_name_info(arg2, varname_from_oparg)
                argrepr = argrepr1 + ", " + argrepr2
                argval = val1, val2
            elif deop in haslocal or deop in hasfree:
                argval, argrepr = _get_name_info(arg, varname_from_oparg)
            elif deop in hascompare:
                argval = cmp_op[arg >> 5]
                argrepr = argval
                if arg & 16:
                    argrepr = f"bool({argrepr})"
            elif deop == CONVERT_VALUE:
                argval = (None, str, repr, ascii)[arg]
                argrepr = ('', 'str', 'repr', 'ascii')[arg]
            elif deop == SET_FUNCTION_ATTRIBUTE:
                argrepr = ', '.join(s for i, s in enumerate(FUNCTION_ATTR_FLAGS)
                                    if arg & (1<<i))
            elif deop == BINARY_OP:
                _, argrepr = _nb_ops[arg]
            elif deop == CALL_INTRINSIC_1:
                argrepr = _intrinsic_1_descs[arg]
            elif deop == CALL_INTRINSIC_2:
                argrepr = _intrinsic_2_descs[arg]
        return argval, argrepr


    @classmethod
    def _create(cls, op, arg, offset, start_offset, starts_line, line_number,
               positions,
               co_consts=None, varname_from_oparg=None, names=None,
               labels_map=None, exceptions_map=None):

        argval, argrepr = cls._get_argval_argrepr(
                               op, arg, offset,
                               co_consts, names, varname_from_oparg, labels_map)
        label = labels_map.get(offset, None)
        instr = Instruction(_all_opname[op], op, arg, argval, argrepr,
                            offset, start_offset, starts_line, line_number,
                            label, positions)
        instr.label_width = 4 + len(str(len(labels_map)))
        instr.exc_handler = exceptions_map.get(offset, None)
        return instr

    @property
    def oparg(self):
        
        return self.arg

    @property
    def baseopcode(self):
        
        return _deoptop(self.opcode)

    @property
    def baseopname(self):
        
        return opname[self.baseopcode]

    @property
    def cache_offset(self):
        
        return self.offset + 2

    @property
    def end_offset(self):
        
        return self.cache_offset + _get_cache_size(_all_opname[self.opcode])*2

    @property
    def jump_target(self):
        
        return _get_jump_target(self.opcode, self.arg, self.offset)

    @property
    def is_jump_target(self):
        
        return self.label is not None

    def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=0,
                           label_width=0):
        
        fields = []
        
        if lineno_width:
            if self.starts_line:
                lineno_fmt = "%%%dd" if self.line_number is not None else "%%%ds"
                lineno_fmt = lineno_fmt % lineno_width
                lineno = self.line_number if self.line_number is not None else '--'
                fields.append(lineno_fmt % lineno)
            else:
                fields.append(' ' * lineno_width)
        
        if self.label is not None:
            lbl = f"L{self.label}:"
            fields.append(f"{lbl:>{label_width}}")
        else:
            fields.append(' ' * label_width)
        
        if offset_width > 0:
            fields.append(f"{repr(self.offset):>{offset_width}}  ")
        
        if mark_as_current:
            fields.append('-->')
        else:
            fields.append('   ')
        
        fields.append(self.opname.ljust(_OPNAME_WIDTH))
        
        if self.arg is not None:
            arg = repr(self.arg)
            
            
            
            opname_excess = max(0, len(self.opname) - _OPNAME_WIDTH)
            fields.append(repr(self.arg).rjust(_OPARG_WIDTH - opname_excess))
            
            if self.argrepr:
                fields.append('(' + self.argrepr + ')')
        return ' '.join(fields).rstrip()

    def __str__(self):
        return self._disassemble()

def get_instructions(x, *, first_line=None, show_caches=False, adaptive=False):
    
    co = _get_code_object(x)
    linestarts = dict(findlinestarts(co))
    if first_line is not None:
        line_offset = first_line - co.co_firstlineno
    else:
        line_offset = 0
    return _get_instructions_bytes(_get_code_array(co, adaptive),
                                   co._varname_from_oparg,
                                   co.co_names, co.co_consts,
                                   linestarts, line_offset,
                                   co_positions=co.co_positions(),
                                   show_caches=show_caches,
                                   original_code=co.co_code)

def _get_const_value(op, arg, co_consts):
    
    assert op in hasconst

    argval = UNKNOWN
    if co_consts is not None:
        argval = co_consts[arg]
    return argval

def _get_const_info(op, arg, co_consts):
    
    argval = _get_const_value(op, arg, co_consts)
    argrepr = repr(argval) if argval is not UNKNOWN else ''
    return argval, argrepr

def _get_name_info(name_index, get_name, **extrainfo):
    
    if get_name is not None:
        argval = get_name(name_index, **extrainfo)
        return argval, argval
    else:
        return UNKNOWN, ''

def _parse_varint(iterator):
    b = next(iterator)
    val = b & 63
    while b&64:
        val <<= 6
        b = next(iterator)
        val |= b&63
    return val

def _parse_exception_table(code):
    iterator = iter(code.co_exceptiontable)
    entries = []
    try:
        while True:
            start = _parse_varint(iterator)*2
            length = _parse_varint(iterator)*2
            end = start + length
            target = _parse_varint(iterator)*2
            dl = _parse_varint(iterator)
            depth = dl >> 1
            lasti = bool(dl&1)
            entries.append(_ExceptionTableEntry(start, end, target, depth, lasti))
    except StopIteration:
        return entries

def _is_backward_jump(op):
    return opname[op] in ('JUMP_BACKWARD',
                          'JUMP_BACKWARD_NO_INTERRUPT',
                          'ENTER_EXECUTOR')

def _get_instructions_bytes(code, varname_from_oparg=None,
                            names=None, co_consts=None,
                            linestarts=None, line_offset=0,
                            exception_entries=(), co_positions=None,
                            show_caches=False, original_code=None):
    
    
    
    
    original_code = original_code or code
    co_positions = co_positions or iter(())
    get_name = None if names is None else names.__getitem__

    def make_labels_map(original_code, exception_entries):
        jump_targets = set(findlabels(original_code))
        labels = set(jump_targets)
        for start, end, target, _, _ in exception_entries:
            labels.add(start)
            labels.add(end)
            labels.add(target)
        labels = sorted(labels)
        labels_map = {offset: i+1 for (i, offset) in enumerate(sorted(labels))}
        for e in exception_entries:
            e.start_label = labels_map[e.start]
            e.end_label = labels_map[e.end]
            e.target_label = labels_map[e.target]
        return labels_map

    labels_map = make_labels_map(original_code, exception_entries)
    label_width = 4 + len(str(len(labels_map)))

    exceptions_map = {}
    for start, end, target, _, _ in exception_entries:
        exceptions_map[start] = labels_map[target]
        exceptions_map[end] = -1

    starts_line = False
    local_line_number = None
    line_number = None
    for offset, start_offset, op, arg in _unpack_opargs(original_code):
        if linestarts is not None:
            starts_line = offset in linestarts
            if starts_line:
                local_line_number = linestarts[offset]
            if local_line_number is not None:
                line_number = local_line_number + line_offset
            else:
                line_number = None
        positions = Positions(*next(co_positions, ()))
        deop = _deoptop(op)
        op = code[offset]

        yield Instruction._create(op, arg, offset, start_offset, starts_line, line_number,
                                  positions, co_consts=co_consts,
                                  varname_from_oparg=varname_from_oparg, names=names,
                                  labels_map=labels_map, exceptions_map=exceptions_map)

        caches = _get_cache_size(_all_opname[deop])
        if not caches:
            continue
        if not show_caches:
            
            for _ in range(caches):
                next(co_positions, ())
            continue
        for name, size in _cache_format[opname[deop]].items():
            for i in range(size):
                offset += 2
                
                
                if i == 0:
                    data = code[offset: offset + 2 * size]
                    argrepr = f"{name}: {int.from_bytes(data, sys.byteorder)}"
                else:
                    argrepr = ""
                yield Instruction(
                    "CACHE", CACHE, 0, None, argrepr, offset, offset, False, None, None,
                    Positions(*next(co_positions, ()))
                )

def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False,
                show_offsets=False):
    
    linestarts = dict(findlinestarts(co))
    exception_entries = _parse_exception_table(co)
    _disassemble_bytes(_get_code_array(co, adaptive),
                       lasti, co._varname_from_oparg,
                       co.co_names, co.co_consts, linestarts, file=file,
                       exception_entries=exception_entries,
                       co_positions=co.co_positions(), show_caches=show_caches,
                       original_code=co.co_code, show_offsets=show_offsets)

def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False, show_offsets=False):
    disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets)
    if depth is None or depth > 0:
        if depth is not None:
            depth = depth - 1
        for x in co.co_consts:
            if hasattr(x, 'co_code'):
                print(file=file)
                print("Disassembly of %r:" % (x,), file=file)
                _disassemble_recursive(
                    x, file=file, depth=depth, show_caches=show_caches,
                    adaptive=adaptive, show_offsets=show_offsets
                )

def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
                       names=None, co_consts=None, linestarts=None,
                       *, file=None, line_offset=0, exception_entries=(),
                       co_positions=None, show_caches=False, original_code=None,
                       show_offsets=False):
    
    if bool(linestarts):
        linestarts_ints = [line for line in linestarts.values() if line is not None]
        show_lineno = len(linestarts_ints) > 0
    else:
        show_lineno = False

    if show_lineno:
        maxlineno = max(linestarts_ints) + line_offset
        if maxlineno >= 1000:
            lineno_width = len(str(maxlineno))
        else:
            lineno_width = 3

        if lineno_width < len(str(None)) and None in linestarts.values():
            lineno_width = len(str(None))
    else:
        lineno_width = 0
    if show_offsets:
        maxoffset = len(code) - 2
        if maxoffset >= 10000:
            offset_width = len(str(maxoffset))
        else:
            offset_width = 4
    else:
        offset_width = 0

    label_width = -1
    for instr in _get_instructions_bytes(code, varname_from_oparg, names,
                                         co_consts, linestarts,
                                         line_offset=line_offset,
                                         exception_entries=exception_entries,
                                         co_positions=co_positions,
                                         show_caches=show_caches,
                                         original_code=original_code):
        new_source_line = (show_lineno and
                           instr.starts_line and
                           instr.offset > 0)
        if new_source_line:
            print(file=file)
        if show_caches:
            is_current_instr = instr.offset == lasti
        else:
            
            is_current_instr = instr.offset <= lasti \
                <= instr.offset + 2 * _get_cache_size(_all_opname[_deoptop(instr.opcode)])
        label_width = getattr(instr, 'label_width', label_width)
        assert label_width >= 0
        print(instr._disassemble(lineno_width, is_current_instr, offset_width, label_width),
              file=file)
    if exception_entries:
        print("ExceptionTable:", file=file)
        for entry in exception_entries:
            lasti = " lasti" if entry.lasti else ""
            start = entry.start_label
            end = entry.end_label
            target = entry.target_label
            print(f"  L{start} to L{end} -> L{target} [{entry.depth}]{lasti}", file=file)

def _disassemble_str(source, **kwargs):
    
    _disassemble_recursive(_try_compile(source, '<dis>'), **kwargs)

disco = disassemble                     



_INT_BITS = 32

_INT_OVERFLOW = 2 ** (_INT_BITS - 1)

def _unpack_opargs(code):
    extended_arg = 0
    extended_args_offset = 0  
    caches = 0
    for i in range(0, len(code), 2):
        
        if caches:
            caches -= 1
            continue
        op = code[i]
        deop = _deoptop(op)
        caches = _get_cache_size(_all_opname[deop])
        if deop in hasarg:
            arg = code[i+1] | extended_arg
            extended_arg = (arg << 8) if deop == EXTENDED_ARG else 0
            
            
            
            if extended_arg >= _INT_OVERFLOW:
                extended_arg -= 2 * _INT_OVERFLOW
        else:
            arg = None
            extended_arg = 0
        if deop == EXTENDED_ARG:
            extended_args_offset += 1
            yield (i, i, op, arg)
        else:
            start_offset = i - extended_args_offset*2
            yield (i, start_offset, op, arg)
            extended_args_offset = 0

def findlabels(code):
    
    labels = []
    for offset, _, op, arg in _unpack_opargs(code):
        if arg is not None:
            label = _get_jump_target(op, arg, offset)
            if label is None:
                continue
            if label not in labels:
                labels.append(label)
    return labels

def findlinestarts(code):
    

    lastline = False 
    for start, end, line in code.co_lines():
        if line is not lastline:
            lastline = line
            yield start, line
    return

def _find_imports(co):
    
    IMPORT_NAME = opmap['IMPORT_NAME']

    consts = co.co_consts
    names = co.co_names
    opargs = [(op, arg) for _, _, op, arg in _unpack_opargs(co.co_code)
                  if op != EXTENDED_ARG]
    for i, (op, oparg) in enumerate(opargs):
        if op == IMPORT_NAME and i >= 2:
            from_op = opargs[i-1]
            level_op = opargs[i-2]
            if (from_op[0] in hasconst and level_op[0] in hasconst):
                level = _get_const_value(level_op[0], level_op[1], consts)
                fromlist = _get_const_value(from_op[0], from_op[1], consts)
                yield (names[oparg], level, fromlist)

def _find_store_names(co):
    
    STORE_OPS = {
        opmap['STORE_NAME'],
        opmap['STORE_GLOBAL']
    }

    names = co.co_names
    for _, _, op, arg in _unpack_opargs(co.co_code):
        if op in STORE_OPS:
            yield names[arg]


class Bytecode:
    
    def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False, show_offsets=False):
        self.codeobj = co = _get_code_object(x)
        if first_line is None:
            self.first_line = co.co_firstlineno
            self._line_offset = 0
        else:
            self.first_line = first_line
            self._line_offset = first_line - co.co_firstlineno
        self._linestarts = dict(findlinestarts(co))
        self._original_object = x
        self.current_offset = current_offset
        self.exception_entries = _parse_exception_table(co)
        self.show_caches = show_caches
        self.adaptive = adaptive
        self.show_offsets = show_offsets

    def __iter__(self):
        co = self.codeobj
        return _get_instructions_bytes(_get_code_array(co, self.adaptive),
                                       co._varname_from_oparg,
                                       co.co_names, co.co_consts,
                                       self._linestarts,
                                       line_offset=self._line_offset,
                                       exception_entries=self.exception_entries,
                                       co_positions=co.co_positions(),
                                       show_caches=self.show_caches,
                                       original_code=co.co_code)

    def __repr__(self):
        return "{}({!r})".format(self.__class__.__name__,
                                 self._original_object)

    @classmethod
    def from_traceback(cls, tb, *, show_caches=False, adaptive=False):
        
        while tb.tb_next:
            tb = tb.tb_next
        return cls(
            tb.tb_frame.f_code, current_offset=tb.tb_lasti, show_caches=show_caches, adaptive=adaptive
        )

    def info(self):
        
        return _format_code_info(self.codeobj)

    def dis(self):
        
        co = self.codeobj
        if self.current_offset is not None:
            offset = self.current_offset
        else:
            offset = -1
        with io.StringIO() as output:
            _disassemble_bytes(_get_code_array(co, self.adaptive),
                               varname_from_oparg=co._varname_from_oparg,
                               names=co.co_names, co_consts=co.co_consts,
                               linestarts=self._linestarts,
                               line_offset=self._line_offset,
                               file=output,
                               lasti=offset,
                               exception_entries=self.exception_entries,
                               co_positions=co.co_positions(),
                               show_caches=self.show_caches,
                               original_code=co.co_code,
                               show_offsets=self.show_offsets)
            return output.getvalue()


def main():
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument('-C', '--show-caches', action='store_true',
                        help='show inline caches')
    parser.add_argument('-O', '--show-offsets', action='store_true',
                        help='show instruction offsets')
    parser.add_argument('infile', type=argparse.FileType('rb'), nargs='?', default='-')
    args = parser.parse_args()
    with args.infile as infile:
        source = infile.read()
    code = compile(source, args.infile.name, "exec")
    dis(code, show_caches=args.show_caches, show_offsets=args.show_offsets)

if __name__ == "__main__":
    main()