PageRenderTime 37ms CodeModel.GetById 14ms app.highlight 19ms RepoModel.GetById 2ms app.codeStats 0ms

/Lib/dis.py

http://unladen-swallow.googlecode.com/
Python | 223 lines | 190 code | 14 blank | 19 comment | 70 complexity | 99c9a8d383d064a8082de44a623edf51 MD5 | raw file
  1"""Disassembler of Python byte code into mnemonics."""
  2
  3import sys
  4import types
  5
  6from opcode import *
  7from opcode import __all__ as _opcodes_all
  8
  9__all__ = ["dis","disassemble","distb","disco"] + _opcodes_all
 10del _opcodes_all
 11
 12def dis(x=None):
 13    """Disassemble classes, methods, functions, or code.
 14
 15    With no argument, disassemble the last traceback.
 16
 17    """
 18    if x is None:
 19        distb()
 20        return
 21    if type(x) is types.InstanceType:
 22        x = x.__class__
 23    if hasattr(x, 'im_func'):
 24        x = x.im_func
 25    if hasattr(x, 'func_code'):
 26        x = x.func_code
 27    if hasattr(x, '__dict__'):
 28        items = x.__dict__.items()
 29        items.sort()
 30        for name, x1 in items:
 31            if type(x1) in (types.MethodType,
 32                            types.FunctionType,
 33                            types.CodeType,
 34                            types.ClassType):
 35                print "Disassembly of %s:" % name
 36                try:
 37                    dis(x1)
 38                except TypeError, msg:
 39                    print "Sorry:", msg
 40                print
 41    elif hasattr(x, 'co_code'):
 42        disassemble(x)
 43    elif isinstance(x, str):
 44        disassemble_string(x)
 45    else:
 46        raise TypeError, \
 47              "don't know how to disassemble %s objects" % \
 48              type(x).__name__
 49
 50def distb(tb=None):
 51    """Disassemble a traceback (default: last traceback)."""
 52    if tb is None:
 53        try:
 54            tb = sys.last_traceback
 55        except AttributeError:
 56            raise RuntimeError, "no last traceback to disassemble"
 57        while tb.tb_next: tb = tb.tb_next
 58    disassemble(tb.tb_frame.f_code, tb.tb_lasti)
 59
 60def disassemble(co, lasti=-1):
 61    """Disassemble a code object."""
 62    code = co.co_code
 63    labels = findlabels(code)
 64    linestarts = dict(findlinestarts(co))
 65    n = len(code)
 66    i = 0
 67    extended_arg = 0
 68    free = None
 69    while i < n:
 70        c = code[i]
 71        op = ord(c)
 72        if i in linestarts:
 73            if i > 0:
 74                print
 75            print "%3d" % linestarts[i],
 76        else:
 77            print '   ',
 78
 79        if i == lasti: print '-->',
 80        else: print '   ',
 81        if i in labels: print '>>',
 82        else: print '  ',
 83        print repr(i).rjust(4),
 84        print opname[op].ljust(20),
 85        i = i+1
 86        if op >= HAVE_ARGUMENT:
 87            oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg
 88            extended_arg = 0
 89            i = i+2
 90            if op == EXTENDED_ARG:
 91                extended_arg = oparg*65536L
 92            print repr(oparg).rjust(5),
 93            if op in hasconst:
 94                print '(' + repr(co.co_consts[oparg]) + ')',
 95            elif op in hasname:
 96                print '(' + co.co_names[oparg] + ')',
 97            elif op in hasjrel:
 98                print '(to ' + repr(i + oparg) + ')',
 99            elif op in haslocal:
100                print '(' + co.co_varnames[oparg] + ')',
101            elif op in hascompare:
102                print '(' + cmp_op[oparg] + ')',
103            elif op in hasfree:
104                if free is None:
105                    free = co.co_cellvars + co.co_freevars
106                print '(' + free[oparg] + ')',
107        print
108
109def disassemble_string(code, lasti=-1, varnames=None, names=None,
110                       constants=None):
111    labels = findlabels(code)
112    n = len(code)
113    i = 0
114    while i < n:
115        c = code[i]
116        op = ord(c)
117        if i == lasti: print '-->',
118        else: print '   ',
119        if i in labels: print '>>',
120        else: print '  ',
121        print repr(i).rjust(4),
122        print opname[op].ljust(15),
123        i = i+1
124        if op >= HAVE_ARGUMENT:
125            oparg = ord(code[i]) + ord(code[i+1])*256
126            i = i+2
127            print repr(oparg).rjust(5),
128            if op in hasconst:
129                if constants:
130                    print '(' + repr(constants[oparg]) + ')',
131                else:
132                    print '(%d)'%oparg,
133            elif op in hasname:
134                if names is not None:
135                    print '(' + names[oparg] + ')',
136                else:
137                    print '(%d)'%oparg,
138            elif op in hasjrel:
139                print '(to ' + repr(i + oparg) + ')',
140            elif op in haslocal:
141                if varnames:
142                    print '(' + varnames[oparg] + ')',
143                else:
144                    print '(%d)' % oparg,
145            elif op in hascompare:
146                print '(' + cmp_op[oparg] + ')',
147        print
148
149disco = disassemble                     # XXX For backwards compatibility
150
151def findlabels(code):
152    """Detect all offsets in a byte code which are jump targets.
153
154    Return the list of offsets.
155
156    """
157    labels = []
158    n = len(code)
159    i = 0
160    while i < n:
161        c = code[i]
162        op = ord(c)
163        i = i+1
164        if op >= HAVE_ARGUMENT:
165            oparg = ord(code[i]) + ord(code[i+1])*256
166            i = i+2
167            label = -1
168            if op in hasjrel:
169                label = i+oparg
170            elif op in hasjabs:
171                label = oparg
172            if label >= 0:
173                if label not in labels:
174                    labels.append(label)
175    return labels
176
177def findlinestarts(code):
178    """Find the offsets in a byte code which are start of lines in the source.
179
180    Generate pairs (offset, lineno) as described in Python/compile.c.
181
182    """
183    byte_increments = [ord(c) for c in code.co_lnotab[0::2]]
184    line_increments = [ord(c) for c in code.co_lnotab[1::2]]
185
186    lastlineno = None
187    lineno = code.co_firstlineno
188    addr = 0
189    for byte_incr, line_incr in zip(byte_increments, line_increments):
190        if byte_incr:
191            if lineno != lastlineno:
192                yield (addr, lineno)
193                lastlineno = lineno
194            addr += byte_incr
195        lineno += line_incr
196    if lineno != lastlineno:
197        yield (addr, lineno)
198
199def _test():
200    """Simple test program to disassemble a file."""
201    if sys.argv[1:]:
202        if sys.argv[2:]:
203            sys.stderr.write("usage: python dis.py [-|file]\n")
204            sys.exit(2)
205        fn = sys.argv[1]
206        if not fn or fn == "-":
207            fn = None
208    else:
209        fn = None
210    if fn is None:
211        f = sys.stdin
212    else:
213        f = open(fn)
214    source = f.read()
215    if fn is not None:
216        f.close()
217    else:
218        fn = "<stdin>"
219    code = compile(source, fn, "exec")
220    dis(code)
221
222if __name__ == "__main__":
223    _test()