diff options
Diffstat (limited to 'Lib/dis.py')
-rw-r--r-- | Lib/dis.py | 229 |
1 files changed, 158 insertions, 71 deletions
diff --git a/Lib/dis.py b/Lib/dis.py index 5aa09c95b6d..f64bae66fb8 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -6,12 +6,24 @@ import types from opcode import * from opcode import __all__ as _opcodes_all -__all__ = ["dis", "disassemble", "distb", "disco", - "findlinestarts", "findlabels"] + _opcodes_all +__all__ = ["code_info", "dis", "disassemble", "distb", "disco", + "findlinestarts", "findlabels", "show_code"] + _opcodes_all del _opcodes_all -_have_code = (types.MethodType, types.FunctionType, types.CodeType, - types.ClassType, type) +_have_code = (types.MethodType, types.FunctionType, types.CodeType, type) + +def _try_compile(source, name): + """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 + """ + try: + c = compile(source, name, 'eval') + except SyntaxError: + c = compile(source, name, 'exec') + return c def dis(x=None): """Disassemble classes, methods, functions, or code. @@ -22,31 +34,29 @@ def dis(x=None): if x is None: distb() return - if isinstance(x, types.InstanceType): - x = x.__class__ - if hasattr(x, 'im_func'): - x = x.im_func - if hasattr(x, 'func_code'): - x = x.func_code - if hasattr(x, '__dict__'): - items = x.__dict__.items() - items.sort() + if hasattr(x, '__func__'): # Method + x = x.__func__ + if hasattr(x, '__code__'): # Function + x = x.__code__ + if hasattr(x, '__dict__'): # Class or module + items = sorted(x.__dict__.items()) for name, x1 in items: if isinstance(x1, _have_code): - print "Disassembly of %s:" % name + print("Disassembly of %s:" % name) try: dis(x1) - except TypeError, msg: - print "Sorry:", msg - print - elif hasattr(x, 'co_code'): + except TypeError as msg: + print("Sorry:", msg) + print() + elif hasattr(x, 'co_code'): # Code object disassemble(x) - elif isinstance(x, str): - disassemble_string(x) + elif isinstance(x, (bytes, bytearray)): # Raw bytecode + _disassemble_bytes(x) + elif isinstance(x, str): # Source code + _disassemble_str(x) else: - raise TypeError, \ - "don't know how to disassemble %s objects" % \ - type(x).__name__ + raise TypeError("don't know how to disassemble %s objects" % + type(x).__name__) def distb(tb=None): """Disassemble a traceback (default: last traceback).""" @@ -54,10 +64,86 @@ def distb(tb=None): try: tb = sys.last_traceback except AttributeError: - raise RuntimeError, "no last traceback to disassemble" + raise RuntimeError("no last traceback to disassemble") while tb.tb_next: tb = tb.tb_next disassemble(tb.tb_frame.f_code, tb.tb_lasti) +# 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. +COMPILER_FLAG_NAMES = { + 1: "OPTIMIZED", + 2: "NEWLOCALS", + 4: "VARARGS", + 8: "VARKEYWORDS", + 16: "NESTED", + 32: "GENERATOR", + 64: "NOFREE", +} + +def pretty_flags(flags): + """Return pretty representation of code 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) + +def code_info(x): + """Formatted details of methods, functions, or code.""" + if hasattr(x, '__func__'): # Method + x = x.__func__ + if hasattr(x, '__code__'): # Function + x = x.__code__ + if isinstance(x, str): # Source code + x = _try_compile(x, "<code_info>") + if hasattr(x, 'co_code'): # Code object + return _format_code_info(x) + else: + raise TypeError("don't know how to disassemble %s objects" % + type(x).__name__) + +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("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): + """Print details of methods, functions, or code to stdout.""" + print(code_info(co)) + def disassemble(co, lasti=-1): """Disassemble a code object.""" code = co.co_code @@ -68,84 +154,86 @@ def disassemble(co, lasti=-1): extended_arg = 0 free = None while i < n: - c = code[i] - op = ord(c) + op = code[i] if i in linestarts: if i > 0: - print - print "%3d" % linestarts[i], + print() + print("%3d" % linestarts[i], end=' ') else: - print ' ', - - if i == lasti: print '-->', - else: print ' ', - if i in labels: print '>>', - else: print ' ', - print repr(i).rjust(4), - print opname[op].ljust(20), + print(' ', end=' ') + + if i == lasti: print('-->', end=' ') + else: print(' ', end=' ') + if i in labels: print('>>', end=' ') + else: print(' ', end=' ') + print(repr(i).rjust(4), end=' ') + print(opname[op].ljust(20), end=' ') i = i+1 if op >= HAVE_ARGUMENT: - oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg + oparg = code[i] + code[i+1]*256 + extended_arg extended_arg = 0 i = i+2 if op == EXTENDED_ARG: - extended_arg = oparg*65536L - print repr(oparg).rjust(5), + extended_arg = oparg*65536 + print(repr(oparg).rjust(5), end=' ') if op in hasconst: - print '(' + repr(co.co_consts[oparg]) + ')', + print('(' + repr(co.co_consts[oparg]) + ')', end=' ') elif op in hasname: - print '(' + co.co_names[oparg] + ')', + print('(' + co.co_names[oparg] + ')', end=' ') elif op in hasjrel: - print '(to ' + repr(i + oparg) + ')', + print('(to ' + repr(i + oparg) + ')', end=' ') elif op in haslocal: - print '(' + co.co_varnames[oparg] + ')', + print('(' + co.co_varnames[oparg] + ')', end=' ') elif op in hascompare: - print '(' + cmp_op[oparg] + ')', + print('(' + cmp_op[oparg] + ')', end=' ') elif op in hasfree: if free is None: free = co.co_cellvars + co.co_freevars - print '(' + free[oparg] + ')', - print + print('(' + free[oparg] + ')', end=' ') + print() -def disassemble_string(code, lasti=-1, varnames=None, names=None, +def _disassemble_bytes(code, lasti=-1, varnames=None, names=None, constants=None): labels = findlabels(code) n = len(code) i = 0 while i < n: - c = code[i] - op = ord(c) - if i == lasti: print '-->', - else: print ' ', - if i in labels: print '>>', - else: print ' ', - print repr(i).rjust(4), - print opname[op].ljust(15), + op = code[i] + if i == lasti: print('-->', end=' ') + else: print(' ', end=' ') + if i in labels: print('>>', end=' ') + else: print(' ', end=' ') + print(repr(i).rjust(4), end=' ') + print(opname[op].ljust(15), end=' ') i = i+1 if op >= HAVE_ARGUMENT: - oparg = ord(code[i]) + ord(code[i+1])*256 + oparg = code[i] + code[i+1]*256 i = i+2 - print repr(oparg).rjust(5), + print(repr(oparg).rjust(5), end=' ') if op in hasconst: if constants: - print '(' + repr(constants[oparg]) + ')', + print('(' + repr(constants[oparg]) + ')', end=' ') else: - print '(%d)'%oparg, + print('(%d)'%oparg, end=' ') elif op in hasname: if names is not None: - print '(' + names[oparg] + ')', + print('(' + names[oparg] + ')', end=' ') else: - print '(%d)'%oparg, + print('(%d)'%oparg, end=' ') elif op in hasjrel: - print '(to ' + repr(i + oparg) + ')', + print('(to ' + repr(i + oparg) + ')', end=' ') elif op in haslocal: if varnames: - print '(' + varnames[oparg] + ')', + print('(' + varnames[oparg] + ')', end=' ') else: - print '(%d)' % oparg, + print('(%d)' % oparg, end=' ') elif op in hascompare: - print '(' + cmp_op[oparg] + ')', - print + print('(' + cmp_op[oparg] + ')', end=' ') + print() + +def _disassemble_str(source): + """Compile the source string, then disassemble the code object.""" + disassemble(_try_compile(source, '<dis>')) disco = disassemble # XXX For backwards compatibility @@ -159,11 +247,10 @@ def findlabels(code): n = len(code) i = 0 while i < n: - c = code[i] - op = ord(c) + op = code[i] i = i+1 if op >= HAVE_ARGUMENT: - oparg = ord(code[i]) + ord(code[i+1])*256 + oparg = code[i] + code[i+1]*256 i = i+2 label = -1 if op in hasjrel: @@ -181,8 +268,8 @@ def findlinestarts(code): Generate pairs (offset, lineno) as described in Python/compile.c. """ - byte_increments = [ord(c) for c in code.co_lnotab[0::2]] - line_increments = [ord(c) for c in code.co_lnotab[1::2]] + byte_increments = list(code.co_lnotab[0::2]) + line_increments = list(code.co_lnotab[1::2]) lastlineno = None lineno = code.co_firstlineno |