Home | History | Annotate | Download | only in Lib
      1 """Disassembler of Python byte code into mnemonics."""
      2 
      3 import sys
      4 import types
      5 
      6 from opcode import *
      7 from opcode import __all__ as _opcodes_all
      8 
      9 __all__ = ["dis", "disassemble", "distb", "disco",
     10            "findlinestarts", "findlabels"] + _opcodes_all
     11 del _opcodes_all
     12 
     13 _have_code = (types.MethodType, types.FunctionType, types.CodeType,
     14               types.ClassType, type)
     15 
     16 def dis(x=None):
     17     """Disassemble classes, methods, functions, or code.
     18 
     19     With no argument, disassemble the last traceback.
     20 
     21     """
     22     if x is None:
     23         distb()
     24         return
     25     if isinstance(x, types.InstanceType):
     26         x = x.__class__
     27     if hasattr(x, 'im_func'):
     28         x = x.im_func
     29     if hasattr(x, 'func_code'):
     30         x = x.func_code
     31     if hasattr(x, '__dict__'):
     32         items = x.__dict__.items()
     33         items.sort()
     34         for name, x1 in items:
     35             if isinstance(x1, _have_code):
     36                 print "Disassembly of %s:" % name
     37                 try:
     38                     dis(x1)
     39                 except TypeError, msg:
     40                     print "Sorry:", msg
     41                 print
     42     elif hasattr(x, 'co_code'):
     43         disassemble(x)
     44     elif isinstance(x, str):
     45         disassemble_string(x)
     46     else:
     47         raise TypeError, \
     48               "don't know how to disassemble %s objects" % \
     49               type(x).__name__
     50 
     51 def distb(tb=None):
     52     """Disassemble a traceback (default: last traceback)."""
     53     if tb is None:
     54         try:
     55             tb = sys.last_traceback
     56         except AttributeError:
     57             raise RuntimeError, "no last traceback to disassemble"
     58         while tb.tb_next: tb = tb.tb_next
     59     disassemble(tb.tb_frame.f_code, tb.tb_lasti)
     60 
     61 def disassemble(co, lasti=-1):
     62     """Disassemble a code object."""
     63     code = co.co_code
     64     labels = findlabels(code)
     65     linestarts = dict(findlinestarts(co))
     66     n = len(code)
     67     i = 0
     68     extended_arg = 0
     69     free = None
     70     while i < n:
     71         c = code[i]
     72         op = ord(c)
     73         if i in linestarts:
     74             if i > 0:
     75                 print
     76             print "%3d" % linestarts[i],
     77         else:
     78             print '   ',
     79 
     80         if i == lasti: print '-->',
     81         else: print '   ',
     82         if i in labels: print '>>',
     83         else: print '  ',
     84         print repr(i).rjust(4),
     85         print opname[op].ljust(20),
     86         i = i+1
     87         if op >= HAVE_ARGUMENT:
     88             oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg
     89             extended_arg = 0
     90             i = i+2
     91             if op == EXTENDED_ARG:
     92                 extended_arg = oparg*65536L
     93             print repr(oparg).rjust(5),
     94             if op in hasconst:
     95                 print '(' + repr(co.co_consts[oparg]) + ')',
     96             elif op in hasname:
     97                 print '(' + co.co_names[oparg] + ')',
     98             elif op in hasjrel:
     99                 print '(to ' + repr(i + oparg) + ')',
    100             elif op in haslocal:
    101                 print '(' + co.co_varnames[oparg] + ')',
    102             elif op in hascompare:
    103                 print '(' + cmp_op[oparg] + ')',
    104             elif op in hasfree:
    105                 if free is None:
    106                     free = co.co_cellvars + co.co_freevars
    107                 print '(' + free[oparg] + ')',
    108         print
    109 
    110 def disassemble_string(code, lasti=-1, varnames=None, names=None,
    111                        constants=None):
    112     labels = findlabels(code)
    113     n = len(code)
    114     i = 0
    115     while i < n:
    116         c = code[i]
    117         op = ord(c)
    118         if i == lasti: print '-->',
    119         else: print '   ',
    120         if i in labels: print '>>',
    121         else: print '  ',
    122         print repr(i).rjust(4),
    123         print opname[op].ljust(15),
    124         i = i+1
    125         if op >= HAVE_ARGUMENT:
    126             oparg = ord(code[i]) + ord(code[i+1])*256
    127             i = i+2
    128             print repr(oparg).rjust(5),
    129             if op in hasconst:
    130                 if constants:
    131                     print '(' + repr(constants[oparg]) + ')',
    132                 else:
    133                     print '(%d)'%oparg,
    134             elif op in hasname:
    135                 if names is not None:
    136                     print '(' + names[oparg] + ')',
    137                 else:
    138                     print '(%d)'%oparg,
    139             elif op in hasjrel:
    140                 print '(to ' + repr(i + oparg) + ')',
    141             elif op in haslocal:
    142                 if varnames:
    143                     print '(' + varnames[oparg] + ')',
    144                 else:
    145                     print '(%d)' % oparg,
    146             elif op in hascompare:
    147                 print '(' + cmp_op[oparg] + ')',
    148         print
    149 
    150 disco = disassemble                     # XXX For backwards compatibility

    151 
    152 def findlabels(code):
    153     """Detect all offsets in a byte code which are jump targets.
    154 
    155     Return the list of offsets.
    156 
    157     """
    158     labels = []
    159     n = len(code)
    160     i = 0
    161     while i < n:
    162         c = code[i]
    163         op = ord(c)
    164         i = i+1
    165         if op >= HAVE_ARGUMENT:
    166             oparg = ord(code[i]) + ord(code[i+1])*256
    167             i = i+2
    168             label = -1
    169             if op in hasjrel:
    170                 label = i+oparg
    171             elif op in hasjabs:
    172                 label = oparg
    173             if label >= 0:
    174                 if label not in labels:
    175                     labels.append(label)
    176     return labels
    177 
    178 def findlinestarts(code):
    179     """Find the offsets in a byte code which are start of lines in the source.
    180 
    181     Generate pairs (offset, lineno) as described in Python/compile.c.
    182 
    183     """
    184     byte_increments = [ord(c) for c in code.co_lnotab[0::2]]
    185     line_increments = [ord(c) for c in code.co_lnotab[1::2]]
    186 
    187     lastlineno = None
    188     lineno = code.co_firstlineno
    189     addr = 0
    190     for byte_incr, line_incr in zip(byte_increments, line_increments):
    191         if byte_incr:
    192             if lineno != lastlineno:
    193                 yield (addr, lineno)
    194                 lastlineno = lineno
    195             addr += byte_incr
    196         lineno += line_incr
    197     if lineno != lastlineno:
    198         yield (addr, lineno)
    199 
    200 def _test():
    201     """Simple test program to disassemble a file."""
    202     if sys.argv[1:]:
    203         if sys.argv[2:]:
    204             sys.stderr.write("usage: python dis.py [-|file]\n")
    205             sys.exit(2)
    206         fn = sys.argv[1]
    207         if not fn or fn == "-":
    208             fn = None
    209     else:
    210         fn = None
    211     if fn is None:
    212         f = sys.stdin
    213     else:
    214         f = open(fn)
    215     source = f.read()
    216     if fn is not None:
    217         f.close()
    218     else:
    219         fn = "<stdin>"
    220     code = compile(source, fn, "exec")
    221     dis(code)
    222 
    223 if __name__ == "__main__":
    224     _test()
    225