1997-11-18 11:47:55 -04:00
|
|
|
"""Disassembler of Python byte code into mnemonics."""
|
1990-12-26 11:40:07 -04:00
|
|
|
|
|
|
|
import sys
|
1997-03-14 00:15:43 -04:00
|
|
|
import types
|
2013-05-06 10:59:20 -03:00
|
|
|
import collections
|
2013-11-06 08:08:36 -04:00
|
|
|
import io
|
1990-12-26 11:40:07 -04:00
|
|
|
|
2003-02-27 17:29:27 -04:00
|
|
|
from opcode import *
|
2022-05-06 11:18:09 -03:00
|
|
|
from opcode import (
|
|
|
|
__all__ as _opcodes_all,
|
|
|
|
_cache_format,
|
|
|
|
_inline_cache_entries,
|
|
|
|
_nb_ops,
|
2023-05-02 23:00:17 -03:00
|
|
|
_intrinsic_1_descs,
|
|
|
|
_intrinsic_2_descs,
|
2022-05-06 11:18:09 -03:00
|
|
|
_specializations,
|
2023-08-16 19:25:18 -03:00
|
|
|
_specialized_opmap,
|
2022-05-06 11:18:09 -03:00
|
|
|
)
|
2003-02-27 17:29:27 -04:00
|
|
|
|
2010-09-10 09:24:24 -03:00
|
|
|
__all__ = ["code_info", "dis", "disassemble", "distb", "disco",
|
2013-05-06 10:59:20 -03:00
|
|
|
"findlinestarts", "findlabels", "show_code",
|
|
|
|
"get_instructions", "Instruction", "Bytecode"] + _opcodes_all
|
2003-02-27 17:29:27 -04:00
|
|
|
del _opcodes_all
|
2001-01-20 15:54:20 -04:00
|
|
|
|
2016-04-23 03:23:52 -03:00
|
|
|
_have_code = (types.MethodType, types.FunctionType, types.CodeType,
|
|
|
|
classmethod, staticmethod, type)
|
2010-04-04 20:26:50 -03:00
|
|
|
|
2023-06-14 12:15:08 -03:00
|
|
|
CONVERT_VALUE = opmap['CONVERT_VALUE']
|
|
|
|
|
2023-06-13 05:51:05 -03:00
|
|
|
SET_FUNCTION_ATTRIBUTE = opmap['SET_FUNCTION_ATTRIBUTE']
|
|
|
|
FUNCTION_ATTR_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure')
|
2018-03-11 06:07:06 -03:00
|
|
|
|
2023-11-24 14:13:25 -04:00
|
|
|
ENTER_EXECUTOR = opmap['ENTER_EXECUTOR']
|
2021-09-15 06:14:15 -03:00
|
|
|
LOAD_CONST = opmap['LOAD_CONST']
|
2023-02-07 18:32:21 -04:00
|
|
|
RETURN_CONST = opmap['RETURN_CONST']
|
2022-03-17 13:14:57 -03:00
|
|
|
LOAD_GLOBAL = opmap['LOAD_GLOBAL']
|
2021-11-11 02:56:22 -04:00
|
|
|
BINARY_OP = opmap['BINARY_OP']
|
2022-03-31 10:14:15 -03:00
|
|
|
JUMP_BACKWARD = opmap['JUMP_BACKWARD']
|
2022-06-21 07:19:26 -03:00
|
|
|
FOR_ITER = opmap['FOR_ITER']
|
2023-02-13 07:24:55 -04:00
|
|
|
SEND = opmap['SEND']
|
2022-06-14 07:36:22 -03:00
|
|
|
LOAD_ATTR = opmap['LOAD_ATTR']
|
2023-04-24 19:22:14 -03:00
|
|
|
LOAD_SUPER_ATTR = opmap['LOAD_SUPER_ATTR']
|
2023-05-02 23:00:17 -03:00
|
|
|
CALL_INTRINSIC_1 = opmap['CALL_INTRINSIC_1']
|
|
|
|
CALL_INTRINSIC_2 = opmap['CALL_INTRINSIC_2']
|
2023-06-05 07:07:04 -03:00
|
|
|
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']
|
2016-10-08 06:34:25 -03:00
|
|
|
|
2022-02-25 08:11:34 -04:00
|
|
|
CACHE = opmap["CACHE"]
|
|
|
|
|
2022-04-19 05:45:08 -03:00
|
|
|
_all_opname = list(opname)
|
|
|
|
_all_opmap = dict(opmap)
|
2023-08-16 19:25:18 -03:00
|
|
|
for name, op in _specialized_opmap.items():
|
2022-04-19 05:45:08 -03:00
|
|
|
# fill opname and opmap
|
2023-08-16 19:25:18 -03:00
|
|
|
assert op < len(_all_opname)
|
|
|
|
_all_opname[op] = name
|
|
|
|
_all_opmap[name] = op
|
2022-04-19 05:45:08 -03:00
|
|
|
|
|
|
|
deoptmap = {
|
|
|
|
specialized: base for base, family in _specializations.items() for specialized in family
|
|
|
|
}
|
|
|
|
|
2010-07-03 04:36:51 -03:00
|
|
|
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:
|
2023-04-15 02:53:31 -03:00
|
|
|
return compile(source, name, 'eval')
|
2010-07-03 04:36:51 -03:00
|
|
|
except SyntaxError:
|
2023-04-15 02:53:31 -03:00
|
|
|
pass
|
|
|
|
return compile(source, name, 'exec')
|
2010-07-03 04:36:51 -03:00
|
|
|
|
2023-11-22 18:36:55 -04:00
|
|
|
def dis(x=None, *, file=None, depth=None, show_caches=False, adaptive=False,
|
|
|
|
show_offsets=False):
|
2017-08-17 23:29:21 -03:00
|
|
|
"""Disassemble classes, methods, functions, and other compiled objects.
|
2001-01-14 19:36:06 -04:00
|
|
|
|
|
|
|
With no argument, disassemble the last traceback.
|
|
|
|
|
2017-08-17 23:29:21 -03:00
|
|
|
Compiled objects currently include generator objects, async generator
|
|
|
|
objects, and coroutine objects, all of which store their code object
|
|
|
|
in a special attribute.
|
2001-01-14 19:36:06 -04:00
|
|
|
"""
|
2002-05-31 21:57:55 -03:00
|
|
|
if x is None:
|
2023-11-22 18:36:55 -04:00
|
|
|
distb(file=file, show_caches=show_caches, adaptive=adaptive,
|
|
|
|
show_offsets=show_offsets)
|
2001-01-14 19:36:06 -04:00
|
|
|
return
|
2017-08-17 23:29:21 -03:00
|
|
|
# Extract functions from methods.
|
|
|
|
if hasattr(x, '__func__'):
|
2007-11-27 06:40:20 -04:00
|
|
|
x = x.__func__
|
2017-08-17 23:29:21 -03:00
|
|
|
# Extract compiled code objects from...
|
|
|
|
if hasattr(x, '__code__'): # ...a function, or
|
2007-02-25 16:55:47 -04:00
|
|
|
x = x.__code__
|
2017-08-17 23:29:21 -03:00
|
|
|
elif hasattr(x, 'gi_code'): #...a generator object, or
|
2014-07-25 10:02:56 -03:00
|
|
|
x = x.gi_code
|
2017-08-17 23:29:21 -03:00
|
|
|
elif hasattr(x, 'ag_code'): #...an asynchronous generator object, or
|
|
|
|
x = x.ag_code
|
|
|
|
elif hasattr(x, 'cr_code'): #...a coroutine.
|
|
|
|
x = x.cr_code
|
|
|
|
# Perform the disassembly.
|
2010-08-17 05:03:36 -03:00
|
|
|
if hasattr(x, '__dict__'): # Class or module
|
Merged revisions 55631-55794 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/branches/p3yk
................
r55636 | neal.norwitz | 2007-05-29 00:06:39 -0700 (Tue, 29 May 2007) | 149 lines
Merged revisions 55506-55635 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r55507 | georg.brandl | 2007-05-22 07:28:17 -0700 (Tue, 22 May 2007) | 2 lines
Remove the "panel" module doc file which has been ignored since 1994.
........
r55522 | mark.hammond | 2007-05-22 19:04:28 -0700 (Tue, 22 May 2007) | 4 lines
Remove definition of PY_UNICODE_TYPE from pyconfig.h, allowing the
definition in unicodeobject.h to be used, giving us the desired
wchar_t in place of 'unsigned short'. As discussed on python-dev.
........
r55525 | neal.norwitz | 2007-05-22 23:35:32 -0700 (Tue, 22 May 2007) | 6 lines
Add -3 option to the interpreter to warn about features that are
deprecated and will be changed/removed in Python 3.0.
This patch is mostly from Anthony. I tweaked some format and added
a little doc.
........
r55527 | neal.norwitz | 2007-05-22 23:57:35 -0700 (Tue, 22 May 2007) | 1 line
Whitespace cleanup
........
r55528 | neal.norwitz | 2007-05-22 23:58:36 -0700 (Tue, 22 May 2007) | 1 line
Add a bunch more deprecation warnings for builtins that are going away in 3.0
........
r55549 | georg.brandl | 2007-05-24 09:49:29 -0700 (Thu, 24 May 2007) | 2 lines
shlex.split() now has an optional "posix" parameter.
........
r55550 | georg.brandl | 2007-05-24 10:33:33 -0700 (Thu, 24 May 2007) | 2 lines
Fix parameter passing.
........
r55555 | facundo.batista | 2007-05-24 10:50:54 -0700 (Thu, 24 May 2007) | 6 lines
Added an optional timeout parameter to urllib.ftpwrapper, with tests
(for this and a basic one, because there weren't any). Changed also
NEWS, but didn't find documentation for this function, assumed it
wasn't public...
........
r55563 | facundo.batista | 2007-05-24 13:01:59 -0700 (Thu, 24 May 2007) | 4 lines
Removed the .recv() in the test, is not necessary, and was
causing problems that didn't have anything to do with was
actually being tested...
........
r55564 | facundo.batista | 2007-05-24 13:51:19 -0700 (Thu, 24 May 2007) | 5 lines
Let's see if reading exactly what is written allow this live
test to pass (now I know why there were so few tests in ftp,
http, etc, :( ).
........
r55567 | facundo.batista | 2007-05-24 20:10:28 -0700 (Thu, 24 May 2007) | 4 lines
Trying to make the tests work in Windows and Solaris, everywhere
else just works
........
r55568 | facundo.batista | 2007-05-24 20:47:19 -0700 (Thu, 24 May 2007) | 4 lines
Fixing stupid error, and introducing a sleep, to see if the
other thread is awakened and finish sending data.
........
r55569 | facundo.batista | 2007-05-24 21:20:22 -0700 (Thu, 24 May 2007) | 4 lines
Commenting out the tests until find out who can test them in
one of the problematic enviroments.
........
r55570 | neal.norwitz | 2007-05-24 22:13:40 -0700 (Thu, 24 May 2007) | 2 lines
Get test passing again by commenting out the reference to the test class.
........
r55575 | vinay.sajip | 2007-05-25 00:05:59 -0700 (Fri, 25 May 2007) | 1 line
Updated docstring for SysLogHandler (#1720726).
........
r55576 | vinay.sajip | 2007-05-25 00:06:55 -0700 (Fri, 25 May 2007) | 1 line
Updated documentation for SysLogHandler (#1720726).
........
r55592 | brett.cannon | 2007-05-25 13:17:15 -0700 (Fri, 25 May 2007) | 3 lines
Remove direct call's to file's constructor and replace them with calls to
open() as ths is considered best practice.
........
r55601 | kristjan.jonsson | 2007-05-26 12:19:50 -0700 (Sat, 26 May 2007) | 1 line
Remove the rgbimgmodule from PCBuild8
........
r55602 | kristjan.jonsson | 2007-05-26 12:31:39 -0700 (Sat, 26 May 2007) | 1 line
Include <windows.h> after python.h, so that WINNT is properly set before windows.h is included. Fixes warnings in PC builds.
........
r55603 | walter.doerwald | 2007-05-26 14:04:13 -0700 (Sat, 26 May 2007) | 2 lines
Fix typo.
........
r55604 | peter.astrand | 2007-05-26 15:18:20 -0700 (Sat, 26 May 2007) | 1 line
Applied patch 1669481, slightly modified: Support close_fds on Win32
........
r55606 | neal.norwitz | 2007-05-26 21:08:54 -0700 (Sat, 26 May 2007) | 2 lines
Add the new function object attribute names from py3k.
........
r55617 | lars.gustaebel | 2007-05-27 12:49:30 -0700 (Sun, 27 May 2007) | 20 lines
Added errors argument to TarFile class that allows the user to
specify an error handling scheme for character conversion. Additional
scheme "utf-8" in read mode. Unicode input filenames are now
supported by design. The values of the pax_headers dictionary are now
limited to unicode objects.
Fixed: The prefix field is no longer used in PAX_FORMAT (in
conformance with POSIX).
Fixed: In read mode use a possible pax header size field.
Fixed: Strip trailing slashes from pax header name values.
Fixed: Give values in user-specified pax_headers precedence when
writing.
Added unicode tests. Added pax/regtype4 member to testtar.tar all
possible number fields in a pax header.
Added two chapters to the documentation about the different formats
tarfile.py supports and how unicode issues are handled.
........
r55618 | raymond.hettinger | 2007-05-27 22:23:22 -0700 (Sun, 27 May 2007) | 1 line
Explain when groupby() issues a new group.
........
r55634 | martin.v.loewis | 2007-05-28 21:01:29 -0700 (Mon, 28 May 2007) | 2 lines
Test pre-commit hook for a link to a .py file.
........
r55635 | martin.v.loewis | 2007-05-28 21:02:03 -0700 (Mon, 28 May 2007) | 2 lines
Revert 55634.
........
................
r55639 | neal.norwitz | 2007-05-29 00:58:11 -0700 (Tue, 29 May 2007) | 1 line
Remove sys.exc_{type,exc_value,exc_traceback}
................
r55641 | neal.norwitz | 2007-05-29 01:03:50 -0700 (Tue, 29 May 2007) | 1 line
Missed one sys.exc_type. I wonder why exc_{value,traceback} were already gone
................
r55642 | neal.norwitz | 2007-05-29 01:08:33 -0700 (Tue, 29 May 2007) | 1 line
Missed more doc for sys.exc_* attrs.
................
r55643 | neal.norwitz | 2007-05-29 01:18:19 -0700 (Tue, 29 May 2007) | 1 line
Remove sys.exc_clear()
................
r55665 | guido.van.rossum | 2007-05-29 19:45:43 -0700 (Tue, 29 May 2007) | 4 lines
Make None, True, False keywords.
We can now also delete all the other places that explicitly forbid
assignment to None, but I'm not going to bother right now.
................
r55666 | guido.van.rossum | 2007-05-29 20:01:51 -0700 (Tue, 29 May 2007) | 3 lines
Found another place that needs check for forbidden names.
Fixed test_syntax.py accordingly (it helped me find that one).
................
r55668 | guido.van.rossum | 2007-05-29 20:41:48 -0700 (Tue, 29 May 2007) | 2 lines
Mark None, True, False as keywords.
................
r55673 | neal.norwitz | 2007-05-29 23:28:25 -0700 (Tue, 29 May 2007) | 3 lines
Get the dis module working on modules again after changing dicts
to not return lists and also new-style classes. Add a test.
................
r55674 | neal.norwitz | 2007-05-29 23:35:45 -0700 (Tue, 29 May 2007) | 1 line
Umm, it helps to add the module that the test uses
................
r55675 | neal.norwitz | 2007-05-29 23:53:05 -0700 (Tue, 29 May 2007) | 4 lines
Try to fix up all the other places that were assigning to True/False.
There's at least one more problem in test.test_xmlrpc. I have other
changes in that file and that should be fixed soon (I hope).
................
r55679 | neal.norwitz | 2007-05-30 00:31:55 -0700 (Wed, 30 May 2007) | 1 line
Fix up another place that was assigning to True/False.
................
r55688 | brett.cannon | 2007-05-30 14:19:47 -0700 (Wed, 30 May 2007) | 2 lines
Ditch MimeWriter.
................
r55692 | brett.cannon | 2007-05-30 14:52:00 -0700 (Wed, 30 May 2007) | 2 lines
Remove the mimify module.
................
r55707 | guido.van.rossum | 2007-05-31 05:08:45 -0700 (Thu, 31 May 2007) | 2 lines
Backport the addition of show_code() to dis.py -- it's too handy.
................
r55708 | guido.van.rossum | 2007-05-31 06:22:57 -0700 (Thu, 31 May 2007) | 7 lines
Fix a fairly long-standing bug in the check for assignment to None (and other
keywords, these days). In 2.5, you could write foo(None=1) without getting
a SyntaxError (although foo()'s definition would have to use **kwds to avoid
getting a runtime error complaining about an unknown keyword of course).
This ought to be backported to 2.5.2 or at least 2.6.
................
r55724 | brett.cannon | 2007-05-31 19:32:41 -0700 (Thu, 31 May 2007) | 2 lines
Remove the cfmfile.
................
r55727 | neal.norwitz | 2007-05-31 22:19:44 -0700 (Thu, 31 May 2007) | 1 line
Remove reload() builtin.
................
r55729 | neal.norwitz | 2007-05-31 22:51:30 -0700 (Thu, 31 May 2007) | 59 lines
Merged revisions 55636-55728 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r55637 | georg.brandl | 2007-05-29 00:16:47 -0700 (Tue, 29 May 2007) | 2 lines
Fix rst markup.
........
r55638 | neal.norwitz | 2007-05-29 00:51:39 -0700 (Tue, 29 May 2007) | 1 line
Fix typo in doc
........
r55671 | neal.norwitz | 2007-05-29 21:53:41 -0700 (Tue, 29 May 2007) | 1 line
Fix indentation (whitespace only).
........
r55676 | thomas.heller | 2007-05-29 23:58:30 -0700 (Tue, 29 May 2007) | 1 line
Fix compiler warnings.
........
r55677 | thomas.heller | 2007-05-30 00:01:25 -0700 (Wed, 30 May 2007) | 2 lines
Correct the name of a field in the WIN32_FIND_DATAA and WIN32_FIND_DATAW structures.
Closes bug #1726026.
........
r55686 | brett.cannon | 2007-05-30 13:46:26 -0700 (Wed, 30 May 2007) | 2 lines
Have MimeWriter raise a DeprecationWarning as per PEP 4 and its documentation.
........
r55690 | brett.cannon | 2007-05-30 14:48:58 -0700 (Wed, 30 May 2007) | 3 lines
Have mimify raise a DeprecationWarning. The docs and PEP 4 have listed the
module as deprecated for a while.
........
r55696 | brett.cannon | 2007-05-30 15:24:28 -0700 (Wed, 30 May 2007) | 2 lines
Have md5 raise a DeprecationWarning as per PEP 4.
........
r55705 | neal.norwitz | 2007-05-30 21:14:22 -0700 (Wed, 30 May 2007) | 1 line
Add some spaces in the example code.
........
r55716 | brett.cannon | 2007-05-31 12:20:00 -0700 (Thu, 31 May 2007) | 2 lines
Have the sha module raise a DeprecationWarning as specified in PEP 4.
........
r55719 | brett.cannon | 2007-05-31 12:40:42 -0700 (Thu, 31 May 2007) | 2 lines
Cause buildtools to raise a DeprecationWarning.
........
r55721 | brett.cannon | 2007-05-31 13:01:11 -0700 (Thu, 31 May 2007) | 2 lines
Have cfmfile raise a DeprecationWarning as per PEP 4.
........
r55726 | neal.norwitz | 2007-05-31 21:56:47 -0700 (Thu, 31 May 2007) | 1 line
Mail if there is an installation failure.
........
................
r55730 | neal.norwitz | 2007-05-31 23:22:07 -0700 (Thu, 31 May 2007) | 2 lines
Remove the code that was missed in rev 55303.
................
r55738 | neal.norwitz | 2007-06-01 19:10:43 -0700 (Fri, 01 Jun 2007) | 1 line
Fix doc breakage
................
r55741 | neal.norwitz | 2007-06-02 00:41:58 -0700 (Sat, 02 Jun 2007) | 1 line
Remove timing module (plus some remnants of other modules).
................
r55742 | neal.norwitz | 2007-06-02 00:51:44 -0700 (Sat, 02 Jun 2007) | 1 line
Remove posixfile module (plus some remnants of other modules).
................
r55744 | neal.norwitz | 2007-06-02 10:18:56 -0700 (Sat, 02 Jun 2007) | 1 line
Fix doc breakage.
................
r55745 | neal.norwitz | 2007-06-02 11:32:16 -0700 (Sat, 02 Jun 2007) | 1 line
Make a whatsnew 3.0 template.
................
r55754 | neal.norwitz | 2007-06-03 23:24:18 -0700 (Sun, 03 Jun 2007) | 1 line
SF #1730441, os._execvpe raises UnboundLocal due to new try/except semantics
................
r55755 | neal.norwitz | 2007-06-03 23:26:00 -0700 (Sun, 03 Jun 2007) | 1 line
Get rid of extra whitespace
................
r55794 | guido.van.rossum | 2007-06-06 15:29:22 -0700 (Wed, 06 Jun 2007) | 3 lines
Make this compile in GCC 2.96, which does not allow interspersing
declarations and code.
................
2007-06-06 20:52:48 -03:00
|
|
|
items = sorted(x.__dict__.items())
|
2001-01-14 19:36:06 -04:00
|
|
|
for name, x1 in items:
|
2010-04-04 20:26:50 -03:00
|
|
|
if isinstance(x1, _have_code):
|
2013-05-06 10:59:20 -03:00
|
|
|
print("Disassembly of %s:" % name, file=file)
|
2001-01-14 19:36:06 -04:00
|
|
|
try:
|
2023-11-22 18:36:55 -04:00
|
|
|
dis(x1, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets)
|
2007-01-10 12:19:56 -04:00
|
|
|
except TypeError as msg:
|
2013-05-06 10:59:20 -03:00
|
|
|
print("Sorry:", msg, file=file)
|
|
|
|
print(file=file)
|
2010-08-17 05:03:36 -03:00
|
|
|
elif hasattr(x, 'co_code'): # Code object
|
2023-11-22 18:36:55 -04:00
|
|
|
_disassemble_recursive(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets)
|
2010-08-17 05:03:36 -03:00
|
|
|
elif isinstance(x, (bytes, bytearray)): # Raw bytecode
|
2023-11-22 18:36:55 -04:00
|
|
|
_disassemble_bytes(x, file=file, show_caches=show_caches, show_offsets=show_offsets)
|
2010-08-17 05:03:36 -03:00
|
|
|
elif isinstance(x, str): # Source code
|
2023-11-22 18:36:55 -04:00
|
|
|
_disassemble_str(x, file=file, depth=depth, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets)
|
2001-01-14 19:36:06 -04:00
|
|
|
else:
|
Merged revisions 55631-55794 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/branches/p3yk
................
r55636 | neal.norwitz | 2007-05-29 00:06:39 -0700 (Tue, 29 May 2007) | 149 lines
Merged revisions 55506-55635 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r55507 | georg.brandl | 2007-05-22 07:28:17 -0700 (Tue, 22 May 2007) | 2 lines
Remove the "panel" module doc file which has been ignored since 1994.
........
r55522 | mark.hammond | 2007-05-22 19:04:28 -0700 (Tue, 22 May 2007) | 4 lines
Remove definition of PY_UNICODE_TYPE from pyconfig.h, allowing the
definition in unicodeobject.h to be used, giving us the desired
wchar_t in place of 'unsigned short'. As discussed on python-dev.
........
r55525 | neal.norwitz | 2007-05-22 23:35:32 -0700 (Tue, 22 May 2007) | 6 lines
Add -3 option to the interpreter to warn about features that are
deprecated and will be changed/removed in Python 3.0.
This patch is mostly from Anthony. I tweaked some format and added
a little doc.
........
r55527 | neal.norwitz | 2007-05-22 23:57:35 -0700 (Tue, 22 May 2007) | 1 line
Whitespace cleanup
........
r55528 | neal.norwitz | 2007-05-22 23:58:36 -0700 (Tue, 22 May 2007) | 1 line
Add a bunch more deprecation warnings for builtins that are going away in 3.0
........
r55549 | georg.brandl | 2007-05-24 09:49:29 -0700 (Thu, 24 May 2007) | 2 lines
shlex.split() now has an optional "posix" parameter.
........
r55550 | georg.brandl | 2007-05-24 10:33:33 -0700 (Thu, 24 May 2007) | 2 lines
Fix parameter passing.
........
r55555 | facundo.batista | 2007-05-24 10:50:54 -0700 (Thu, 24 May 2007) | 6 lines
Added an optional timeout parameter to urllib.ftpwrapper, with tests
(for this and a basic one, because there weren't any). Changed also
NEWS, but didn't find documentation for this function, assumed it
wasn't public...
........
r55563 | facundo.batista | 2007-05-24 13:01:59 -0700 (Thu, 24 May 2007) | 4 lines
Removed the .recv() in the test, is not necessary, and was
causing problems that didn't have anything to do with was
actually being tested...
........
r55564 | facundo.batista | 2007-05-24 13:51:19 -0700 (Thu, 24 May 2007) | 5 lines
Let's see if reading exactly what is written allow this live
test to pass (now I know why there were so few tests in ftp,
http, etc, :( ).
........
r55567 | facundo.batista | 2007-05-24 20:10:28 -0700 (Thu, 24 May 2007) | 4 lines
Trying to make the tests work in Windows and Solaris, everywhere
else just works
........
r55568 | facundo.batista | 2007-05-24 20:47:19 -0700 (Thu, 24 May 2007) | 4 lines
Fixing stupid error, and introducing a sleep, to see if the
other thread is awakened and finish sending data.
........
r55569 | facundo.batista | 2007-05-24 21:20:22 -0700 (Thu, 24 May 2007) | 4 lines
Commenting out the tests until find out who can test them in
one of the problematic enviroments.
........
r55570 | neal.norwitz | 2007-05-24 22:13:40 -0700 (Thu, 24 May 2007) | 2 lines
Get test passing again by commenting out the reference to the test class.
........
r55575 | vinay.sajip | 2007-05-25 00:05:59 -0700 (Fri, 25 May 2007) | 1 line
Updated docstring for SysLogHandler (#1720726).
........
r55576 | vinay.sajip | 2007-05-25 00:06:55 -0700 (Fri, 25 May 2007) | 1 line
Updated documentation for SysLogHandler (#1720726).
........
r55592 | brett.cannon | 2007-05-25 13:17:15 -0700 (Fri, 25 May 2007) | 3 lines
Remove direct call's to file's constructor and replace them with calls to
open() as ths is considered best practice.
........
r55601 | kristjan.jonsson | 2007-05-26 12:19:50 -0700 (Sat, 26 May 2007) | 1 line
Remove the rgbimgmodule from PCBuild8
........
r55602 | kristjan.jonsson | 2007-05-26 12:31:39 -0700 (Sat, 26 May 2007) | 1 line
Include <windows.h> after python.h, so that WINNT is properly set before windows.h is included. Fixes warnings in PC builds.
........
r55603 | walter.doerwald | 2007-05-26 14:04:13 -0700 (Sat, 26 May 2007) | 2 lines
Fix typo.
........
r55604 | peter.astrand | 2007-05-26 15:18:20 -0700 (Sat, 26 May 2007) | 1 line
Applied patch 1669481, slightly modified: Support close_fds on Win32
........
r55606 | neal.norwitz | 2007-05-26 21:08:54 -0700 (Sat, 26 May 2007) | 2 lines
Add the new function object attribute names from py3k.
........
r55617 | lars.gustaebel | 2007-05-27 12:49:30 -0700 (Sun, 27 May 2007) | 20 lines
Added errors argument to TarFile class that allows the user to
specify an error handling scheme for character conversion. Additional
scheme "utf-8" in read mode. Unicode input filenames are now
supported by design. The values of the pax_headers dictionary are now
limited to unicode objects.
Fixed: The prefix field is no longer used in PAX_FORMAT (in
conformance with POSIX).
Fixed: In read mode use a possible pax header size field.
Fixed: Strip trailing slashes from pax header name values.
Fixed: Give values in user-specified pax_headers precedence when
writing.
Added unicode tests. Added pax/regtype4 member to testtar.tar all
possible number fields in a pax header.
Added two chapters to the documentation about the different formats
tarfile.py supports and how unicode issues are handled.
........
r55618 | raymond.hettinger | 2007-05-27 22:23:22 -0700 (Sun, 27 May 2007) | 1 line
Explain when groupby() issues a new group.
........
r55634 | martin.v.loewis | 2007-05-28 21:01:29 -0700 (Mon, 28 May 2007) | 2 lines
Test pre-commit hook for a link to a .py file.
........
r55635 | martin.v.loewis | 2007-05-28 21:02:03 -0700 (Mon, 28 May 2007) | 2 lines
Revert 55634.
........
................
r55639 | neal.norwitz | 2007-05-29 00:58:11 -0700 (Tue, 29 May 2007) | 1 line
Remove sys.exc_{type,exc_value,exc_traceback}
................
r55641 | neal.norwitz | 2007-05-29 01:03:50 -0700 (Tue, 29 May 2007) | 1 line
Missed one sys.exc_type. I wonder why exc_{value,traceback} were already gone
................
r55642 | neal.norwitz | 2007-05-29 01:08:33 -0700 (Tue, 29 May 2007) | 1 line
Missed more doc for sys.exc_* attrs.
................
r55643 | neal.norwitz | 2007-05-29 01:18:19 -0700 (Tue, 29 May 2007) | 1 line
Remove sys.exc_clear()
................
r55665 | guido.van.rossum | 2007-05-29 19:45:43 -0700 (Tue, 29 May 2007) | 4 lines
Make None, True, False keywords.
We can now also delete all the other places that explicitly forbid
assignment to None, but I'm not going to bother right now.
................
r55666 | guido.van.rossum | 2007-05-29 20:01:51 -0700 (Tue, 29 May 2007) | 3 lines
Found another place that needs check for forbidden names.
Fixed test_syntax.py accordingly (it helped me find that one).
................
r55668 | guido.van.rossum | 2007-05-29 20:41:48 -0700 (Tue, 29 May 2007) | 2 lines
Mark None, True, False as keywords.
................
r55673 | neal.norwitz | 2007-05-29 23:28:25 -0700 (Tue, 29 May 2007) | 3 lines
Get the dis module working on modules again after changing dicts
to not return lists and also new-style classes. Add a test.
................
r55674 | neal.norwitz | 2007-05-29 23:35:45 -0700 (Tue, 29 May 2007) | 1 line
Umm, it helps to add the module that the test uses
................
r55675 | neal.norwitz | 2007-05-29 23:53:05 -0700 (Tue, 29 May 2007) | 4 lines
Try to fix up all the other places that were assigning to True/False.
There's at least one more problem in test.test_xmlrpc. I have other
changes in that file and that should be fixed soon (I hope).
................
r55679 | neal.norwitz | 2007-05-30 00:31:55 -0700 (Wed, 30 May 2007) | 1 line
Fix up another place that was assigning to True/False.
................
r55688 | brett.cannon | 2007-05-30 14:19:47 -0700 (Wed, 30 May 2007) | 2 lines
Ditch MimeWriter.
................
r55692 | brett.cannon | 2007-05-30 14:52:00 -0700 (Wed, 30 May 2007) | 2 lines
Remove the mimify module.
................
r55707 | guido.van.rossum | 2007-05-31 05:08:45 -0700 (Thu, 31 May 2007) | 2 lines
Backport the addition of show_code() to dis.py -- it's too handy.
................
r55708 | guido.van.rossum | 2007-05-31 06:22:57 -0700 (Thu, 31 May 2007) | 7 lines
Fix a fairly long-standing bug in the check for assignment to None (and other
keywords, these days). In 2.5, you could write foo(None=1) without getting
a SyntaxError (although foo()'s definition would have to use **kwds to avoid
getting a runtime error complaining about an unknown keyword of course).
This ought to be backported to 2.5.2 or at least 2.6.
................
r55724 | brett.cannon | 2007-05-31 19:32:41 -0700 (Thu, 31 May 2007) | 2 lines
Remove the cfmfile.
................
r55727 | neal.norwitz | 2007-05-31 22:19:44 -0700 (Thu, 31 May 2007) | 1 line
Remove reload() builtin.
................
r55729 | neal.norwitz | 2007-05-31 22:51:30 -0700 (Thu, 31 May 2007) | 59 lines
Merged revisions 55636-55728 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r55637 | georg.brandl | 2007-05-29 00:16:47 -0700 (Tue, 29 May 2007) | 2 lines
Fix rst markup.
........
r55638 | neal.norwitz | 2007-05-29 00:51:39 -0700 (Tue, 29 May 2007) | 1 line
Fix typo in doc
........
r55671 | neal.norwitz | 2007-05-29 21:53:41 -0700 (Tue, 29 May 2007) | 1 line
Fix indentation (whitespace only).
........
r55676 | thomas.heller | 2007-05-29 23:58:30 -0700 (Tue, 29 May 2007) | 1 line
Fix compiler warnings.
........
r55677 | thomas.heller | 2007-05-30 00:01:25 -0700 (Wed, 30 May 2007) | 2 lines
Correct the name of a field in the WIN32_FIND_DATAA and WIN32_FIND_DATAW structures.
Closes bug #1726026.
........
r55686 | brett.cannon | 2007-05-30 13:46:26 -0700 (Wed, 30 May 2007) | 2 lines
Have MimeWriter raise a DeprecationWarning as per PEP 4 and its documentation.
........
r55690 | brett.cannon | 2007-05-30 14:48:58 -0700 (Wed, 30 May 2007) | 3 lines
Have mimify raise a DeprecationWarning. The docs and PEP 4 have listed the
module as deprecated for a while.
........
r55696 | brett.cannon | 2007-05-30 15:24:28 -0700 (Wed, 30 May 2007) | 2 lines
Have md5 raise a DeprecationWarning as per PEP 4.
........
r55705 | neal.norwitz | 2007-05-30 21:14:22 -0700 (Wed, 30 May 2007) | 1 line
Add some spaces in the example code.
........
r55716 | brett.cannon | 2007-05-31 12:20:00 -0700 (Thu, 31 May 2007) | 2 lines
Have the sha module raise a DeprecationWarning as specified in PEP 4.
........
r55719 | brett.cannon | 2007-05-31 12:40:42 -0700 (Thu, 31 May 2007) | 2 lines
Cause buildtools to raise a DeprecationWarning.
........
r55721 | brett.cannon | 2007-05-31 13:01:11 -0700 (Thu, 31 May 2007) | 2 lines
Have cfmfile raise a DeprecationWarning as per PEP 4.
........
r55726 | neal.norwitz | 2007-05-31 21:56:47 -0700 (Thu, 31 May 2007) | 1 line
Mail if there is an installation failure.
........
................
r55730 | neal.norwitz | 2007-05-31 23:22:07 -0700 (Thu, 31 May 2007) | 2 lines
Remove the code that was missed in rev 55303.
................
r55738 | neal.norwitz | 2007-06-01 19:10:43 -0700 (Fri, 01 Jun 2007) | 1 line
Fix doc breakage
................
r55741 | neal.norwitz | 2007-06-02 00:41:58 -0700 (Sat, 02 Jun 2007) | 1 line
Remove timing module (plus some remnants of other modules).
................
r55742 | neal.norwitz | 2007-06-02 00:51:44 -0700 (Sat, 02 Jun 2007) | 1 line
Remove posixfile module (plus some remnants of other modules).
................
r55744 | neal.norwitz | 2007-06-02 10:18:56 -0700 (Sat, 02 Jun 2007) | 1 line
Fix doc breakage.
................
r55745 | neal.norwitz | 2007-06-02 11:32:16 -0700 (Sat, 02 Jun 2007) | 1 line
Make a whatsnew 3.0 template.
................
r55754 | neal.norwitz | 2007-06-03 23:24:18 -0700 (Sun, 03 Jun 2007) | 1 line
SF #1730441, os._execvpe raises UnboundLocal due to new try/except semantics
................
r55755 | neal.norwitz | 2007-06-03 23:26:00 -0700 (Sun, 03 Jun 2007) | 1 line
Get rid of extra whitespace
................
r55794 | guido.van.rossum | 2007-06-06 15:29:22 -0700 (Wed, 06 Jun 2007) | 3 lines
Make this compile in GCC 2.96, which does not allow interspersing
declarations and code.
................
2007-06-06 20:52:48 -03:00
|
|
|
raise TypeError("don't know how to disassemble %s objects" %
|
|
|
|
type(x).__name__)
|
1990-12-26 11:40:07 -04:00
|
|
|
|
2023-11-22 18:36:55 -04:00
|
|
|
def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets=False):
|
2001-01-14 19:36:06 -04:00
|
|
|
"""Disassemble a traceback (default: last traceback)."""
|
2002-05-31 21:57:55 -03:00
|
|
|
if tb is None:
|
2001-01-14 19:36:06 -04:00
|
|
|
try:
|
2023-03-18 08:47:11 -03:00
|
|
|
if hasattr(sys, 'last_exc'):
|
|
|
|
tb = sys.last_exc.__traceback__
|
|
|
|
else:
|
|
|
|
tb = sys.last_traceback
|
2001-01-14 19:36:06 -04:00
|
|
|
except AttributeError:
|
2017-04-05 03:37:24 -03:00
|
|
|
raise RuntimeError("no last traceback to disassemble") from None
|
2001-01-14 19:36:06 -04:00
|
|
|
while tb.tb_next: tb = tb.tb_next
|
2023-11-22 18:36:55 -04:00
|
|
|
disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file, show_caches=show_caches, adaptive=adaptive, show_offsets=show_offsets)
|
1990-12-26 11:40:07 -04:00
|
|
|
|
2010-08-17 07:18:16 -03:00
|
|
|
# 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 = {
|
2007-05-29 23:07:00 -03:00
|
|
|
1: "OPTIMIZED",
|
|
|
|
2: "NEWLOCALS",
|
|
|
|
4: "VARARGS",
|
|
|
|
8: "VARKEYWORDS",
|
|
|
|
16: "NESTED",
|
|
|
|
32: "GENERATOR",
|
|
|
|
64: "NOFREE",
|
2015-05-11 23:57:16 -03:00
|
|
|
128: "COROUTINE",
|
|
|
|
256: "ITERABLE_COROUTINE",
|
2016-09-09 02:01:51 -03:00
|
|
|
512: "ASYNC_GENERATOR",
|
2007-05-29 23:07:00 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
def pretty_flags(flags):
|
|
|
|
"""Return pretty representation of code flags."""
|
|
|
|
names = []
|
|
|
|
for i in range(32):
|
|
|
|
flag = 1<<i
|
|
|
|
if flags & flag:
|
2010-08-17 07:18:16 -03:00
|
|
|
names.append(COMPILER_FLAG_NAMES.get(flag, hex(flag)))
|
2007-05-29 23:07:00 -03:00
|
|
|
flags ^= flag
|
|
|
|
if not flags:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
names.append(hex(flags))
|
|
|
|
return ", ".join(names)
|
|
|
|
|
2021-09-14 06:09:05 -03:00
|
|
|
class _Unknown:
|
|
|
|
def __repr__(self):
|
|
|
|
return "<unknown>"
|
|
|
|
|
|
|
|
# Sentinel to represent values that cannot be calculated
|
|
|
|
UNKNOWN = _Unknown()
|
|
|
|
|
2013-05-06 10:59:20 -03:00
|
|
|
def _get_code_object(x):
|
2017-08-17 23:29:21 -03:00
|
|
|
"""Helper to handle methods, compiled or raw code objects, and strings."""
|
|
|
|
# Extract functions from methods.
|
|
|
|
if hasattr(x, '__func__'):
|
2010-08-17 05:03:36 -03:00
|
|
|
x = x.__func__
|
2017-08-17 23:29:21 -03:00
|
|
|
# Extract compiled code objects from...
|
|
|
|
if hasattr(x, '__code__'): # ...a function, or
|
2010-08-17 05:03:36 -03:00
|
|
|
x = x.__code__
|
2017-08-17 23:29:21 -03:00
|
|
|
elif hasattr(x, 'gi_code'): #...a generator object, or
|
2014-07-25 10:02:56 -03:00
|
|
|
x = x.gi_code
|
2017-08-17 23:29:21 -03:00
|
|
|
elif hasattr(x, 'ag_code'): #...an asynchronous generator object, or
|
|
|
|
x = x.ag_code
|
|
|
|
elif hasattr(x, 'cr_code'): #...a coroutine.
|
|
|
|
x = x.cr_code
|
|
|
|
# Handle source code.
|
|
|
|
if isinstance(x, str):
|
2013-05-06 10:59:20 -03:00
|
|
|
x = _try_compile(x, "<disassembly>")
|
2017-08-17 23:29:21 -03:00
|
|
|
# By now, if we don't have a code object, we can't disassemble x.
|
|
|
|
if hasattr(x, 'co_code'):
|
2013-05-06 10:59:20 -03:00
|
|
|
return x
|
|
|
|
raise TypeError("don't know how to disassemble %s objects" %
|
|
|
|
type(x).__name__)
|
|
|
|
|
2022-04-19 05:45:08 -03:00
|
|
|
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
|
|
|
|
|
2013-05-06 10:59:20 -03:00
|
|
|
def code_info(x):
|
|
|
|
"""Formatted details of methods, functions, or code."""
|
|
|
|
return _format_code_info(_get_code_object(x))
|
2010-08-17 05:03:36 -03:00
|
|
|
|
|
|
|
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)
|
2019-04-29 09:36:57 -03:00
|
|
|
lines.append("Positional-only arguments: %s" % co.co_posonlyargcount)
|
2010-08-17 05:03:36 -03:00
|
|
|
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))
|
2007-05-29 23:07:00 -03:00
|
|
|
if co.co_consts:
|
2010-08-17 05:03:36 -03:00
|
|
|
lines.append("Constants:")
|
2007-05-29 23:07:00 -03:00
|
|
|
for i_c in enumerate(co.co_consts):
|
2010-08-17 05:03:36 -03:00
|
|
|
lines.append("%4d: %r" % i_c)
|
2007-05-29 23:07:00 -03:00
|
|
|
if co.co_names:
|
2010-08-17 05:03:36 -03:00
|
|
|
lines.append("Names:")
|
2007-05-29 23:07:00 -03:00
|
|
|
for i_n in enumerate(co.co_names):
|
2010-08-17 05:03:36 -03:00
|
|
|
lines.append("%4d: %s" % i_n)
|
2007-05-29 23:07:00 -03:00
|
|
|
if co.co_varnames:
|
2010-08-17 05:03:36 -03:00
|
|
|
lines.append("Variable names:")
|
2007-05-29 23:07:00 -03:00
|
|
|
for i_n in enumerate(co.co_varnames):
|
2010-08-17 05:03:36 -03:00
|
|
|
lines.append("%4d: %s" % i_n)
|
2007-05-29 23:07:00 -03:00
|
|
|
if co.co_freevars:
|
2010-08-17 05:03:36 -03:00
|
|
|
lines.append("Free variables:")
|
2007-05-29 23:07:00 -03:00
|
|
|
for i_n in enumerate(co.co_freevars):
|
2010-08-17 05:03:36 -03:00
|
|
|
lines.append("%4d: %s" % i_n)
|
2007-05-29 23:07:00 -03:00
|
|
|
if co.co_cellvars:
|
2010-08-17 05:03:36 -03:00
|
|
|
lines.append("Cell variables:")
|
2007-05-29 23:07:00 -03:00
|
|
|
for i_n in enumerate(co.co_cellvars):
|
2010-08-17 05:03:36 -03:00
|
|
|
lines.append("%4d: %s" % i_n)
|
|
|
|
return "\n".join(lines)
|
|
|
|
|
2013-05-06 10:59:20 -03:00
|
|
|
def show_code(co, *, file=None):
|
2013-08-23 16:41:39 -03:00
|
|
|
"""Print details of methods, functions, or code to *file*.
|
|
|
|
|
|
|
|
If *file* is not provided, the output is printed on stdout.
|
|
|
|
"""
|
2013-05-06 10:59:20 -03:00
|
|
|
print(code_info(co), file=file)
|
2007-05-29 23:07:00 -03:00
|
|
|
|
2021-07-04 16:05:05 -03:00
|
|
|
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',
|
2023-06-11 12:50:34 -03:00
|
|
|
'start_offset',
|
2021-07-04 16:05:05 -03:00
|
|
|
'starts_line',
|
2023-08-25 05:31:26 -03:00
|
|
|
'line_number',
|
2023-11-22 18:36:55 -04:00
|
|
|
'label',
|
2021-07-04 16:05:05 -03:00
|
|
|
'positions'
|
|
|
|
],
|
2023-11-22 18:36:55 -04:00
|
|
|
defaults=[None, None]
|
2021-07-04 16:05:05 -03:00
|
|
|
)
|
2013-05-06 10:59:20 -03:00
|
|
|
|
2015-08-18 02:04:45 -03:00
|
|
|
_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"
|
2023-06-11 12:50:34 -03:00
|
|
|
_Instruction.start_offset.__doc__ = (
|
|
|
|
"Start index of operation within bytecode sequence, including extended args if present; "
|
|
|
|
"otherwise equal to Instruction.offset"
|
|
|
|
)
|
2023-08-25 05:31:26 -03:00
|
|
|
_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"
|
2023-11-22 18:36:55 -04:00
|
|
|
_Instruction.label.__doc__ = "A label (int > 0) if this instruction is a jump target, otherwise None"
|
2021-07-04 16:05:05 -03:00
|
|
|
_Instruction.positions.__doc__ = "dis.Positions object holding the span of source code covered by this instruction"
|
2015-08-18 02:04:45 -03:00
|
|
|
|
2023-11-22 18:36:55 -04:00
|
|
|
_ExceptionTableEntryBase = collections.namedtuple("_ExceptionTableEntryBase",
|
2021-05-07 11:19:19 -03:00
|
|
|
"start end target depth lasti")
|
|
|
|
|
2023-11-22 18:36:55 -04:00
|
|
|
class _ExceptionTableEntry(_ExceptionTableEntryBase):
|
|
|
|
pass
|
|
|
|
|
2022-05-02 12:51:17 -03:00
|
|
|
_OPNAME_WIDTH = 20
|
2017-04-19 14:36:31 -03:00
|
|
|
_OPARG_WIDTH = 5
|
|
|
|
|
2023-07-27 10:15:25 -03:00
|
|
|
def _get_cache_size(opname):
|
|
|
|
return _inline_cache_entries.get(opname, 0)
|
|
|
|
|
2023-06-11 12:50:34 -03:00
|
|
|
def _get_jump_target(op, arg, offset):
|
|
|
|
"""Gets the bytecode offset of the jump target if this is a jump instruction.
|
|
|
|
|
|
|
|
Otherwise return None.
|
|
|
|
"""
|
|
|
|
deop = _deoptop(op)
|
2023-07-27 10:15:25 -03:00
|
|
|
caches = _get_cache_size(_all_opname[deop])
|
2023-06-11 12:50:34 -03:00
|
|
|
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
|
|
|
|
|
2013-05-06 10:59:20 -03:00
|
|
|
class Instruction(_Instruction):
|
2023-06-11 12:50:34 -03:00
|
|
|
"""Details for a bytecode operation.
|
2013-05-06 10:59:20 -03:00
|
|
|
|
|
|
|
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
|
2023-06-11 12:50:34 -03:00
|
|
|
start_offset - start index of operation within bytecode sequence including extended args if present;
|
|
|
|
otherwise equal to Instruction.offset
|
2023-08-25 05:31:26 -03:00
|
|
|
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
|
2023-11-22 18:36:55 -04:00
|
|
|
label - A label if this instruction is a jump target, otherwise None
|
2021-07-04 16:05:05 -03:00
|
|
|
positions - Optional dis.Positions object holding the span of source code
|
|
|
|
covered by this instruction
|
2013-05-06 10:59:20 -03:00
|
|
|
"""
|
|
|
|
|
2023-11-12 10:06:02 -04:00
|
|
|
@staticmethod
|
2023-11-22 18:36:55 -04:00
|
|
|
def _get_argval_argrepr(op, arg, offset, co_consts, names, varname_from_oparg,
|
|
|
|
labels_map):
|
2023-11-12 10:06:02 -04:00
|
|
|
get_name = None if names is None else names.__getitem__
|
|
|
|
argval = None
|
|
|
|
argrepr = ''
|
|
|
|
deop = _deoptop(op)
|
|
|
|
if arg is not None:
|
|
|
|
# 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.
|
|
|
|
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
|
2023-11-22 18:36:55 -04:00
|
|
|
argrepr = f"to L{labels_map[argval]}"
|
2023-11-12 10:06:02 -04:00
|
|
|
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
|
2023-11-24 14:13:25 -04:00
|
|
|
if deop == ENTER_EXECUTOR:
|
|
|
|
argval += 2
|
2023-11-22 18:36:55 -04:00
|
|
|
argrepr = f"to L{labels_map[argval]}"
|
2023-11-12 10:06:02 -04:00
|
|
|
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,
|
2023-11-22 18:36:55 -04:00
|
|
|
positions,
|
|
|
|
co_consts=None, varname_from_oparg=None, names=None,
|
|
|
|
labels_map=None, exceptions_map=None):
|
|
|
|
|
2023-11-12 10:06:02 -04:00
|
|
|
argval, argrepr = cls._get_argval_argrepr(
|
|
|
|
op, arg, offset,
|
2023-11-22 18:36:55 -04:00
|
|
|
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)
|
2023-12-03 08:12:49 -04:00
|
|
|
instr.label_width = 4 + len(str(len(labels_map)))
|
2023-11-22 18:36:55 -04:00
|
|
|
instr.exc_handler = exceptions_map.get(offset, None)
|
|
|
|
return instr
|
2023-11-12 10:06:02 -04:00
|
|
|
|
2023-06-11 12:50:34 -03:00
|
|
|
@property
|
|
|
|
def oparg(self):
|
|
|
|
"""Alias for Instruction.arg."""
|
|
|
|
return self.arg
|
|
|
|
|
|
|
|
@property
|
|
|
|
def baseopcode(self):
|
|
|
|
"""Numeric code for the base operation if operation is specialized.
|
|
|
|
|
|
|
|
Otherwise equal to Instruction.opcode.
|
|
|
|
"""
|
|
|
|
return _deoptop(self.opcode)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def baseopname(self):
|
|
|
|
"""Human readable name for the base operation if operation is specialized.
|
|
|
|
|
|
|
|
Otherwise equal to Instruction.opname.
|
|
|
|
"""
|
|
|
|
return opname[self.baseopcode]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def cache_offset(self):
|
|
|
|
"""Start index of the cache entries following the operation."""
|
|
|
|
return self.offset + 2
|
|
|
|
|
|
|
|
@property
|
|
|
|
def end_offset(self):
|
|
|
|
"""End index of the cache entries following the operation."""
|
2023-07-27 10:15:25 -03:00
|
|
|
return self.cache_offset + _get_cache_size(_all_opname[self.opcode])*2
|
2023-06-11 12:50:34 -03:00
|
|
|
|
|
|
|
@property
|
|
|
|
def jump_target(self):
|
|
|
|
"""Bytecode index of the jump target if this is a jump operation.
|
|
|
|
|
|
|
|
Otherwise return None.
|
|
|
|
"""
|
|
|
|
return _get_jump_target(self.opcode, self.arg, self.offset)
|
|
|
|
|
2023-11-22 18:36:55 -04:00
|
|
|
@property
|
|
|
|
def is_jump_target(self):
|
|
|
|
"""True if other code jumps to here, otherwise False"""
|
|
|
|
return self.label is not None
|
|
|
|
|
2023-12-03 08:12:49 -04:00
|
|
|
def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=0,
|
|
|
|
label_width=0):
|
2023-06-11 12:50:34 -03:00
|
|
|
"""Format instruction details for inclusion in disassembly output.
|
2013-05-06 10:59:20 -03:00
|
|
|
|
|
|
|
*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
|
2017-04-19 14:36:31 -03:00
|
|
|
*offset_width* sets the width of the instruction offset field
|
2023-12-03 08:12:49 -04:00
|
|
|
*label_width* sets the width of the label field
|
2013-05-06 10:59:20 -03:00
|
|
|
"""
|
|
|
|
fields = []
|
|
|
|
# Column: Source code line number
|
|
|
|
if lineno_width:
|
2023-08-25 05:31:26 -03:00
|
|
|
if self.starts_line:
|
|
|
|
lineno_fmt = "%%%dd" if self.line_number is not None else "%%%ds"
|
|
|
|
lineno_fmt = lineno_fmt % lineno_width
|
2023-11-23 10:34:27 -04:00
|
|
|
lineno = self.line_number if self.line_number is not None else '--'
|
|
|
|
fields.append(lineno_fmt % lineno)
|
2013-05-06 10:59:20 -03:00
|
|
|
else:
|
|
|
|
fields.append(' ' * lineno_width)
|
2023-11-22 18:36:55 -04:00
|
|
|
# Column: Label
|
|
|
|
if self.label is not None:
|
|
|
|
lbl = f"L{self.label}:"
|
2023-12-03 08:12:49 -04:00
|
|
|
fields.append(f"{lbl:>{label_width}}")
|
2023-11-22 18:36:55 -04:00
|
|
|
else:
|
2023-12-03 08:12:49 -04:00
|
|
|
fields.append(' ' * label_width)
|
2023-11-22 18:36:55 -04:00
|
|
|
# Column: Instruction offset from start of code sequence
|
|
|
|
if offset_width > 0:
|
|
|
|
fields.append(f"{repr(self.offset):>{offset_width}} ")
|
2013-05-06 10:59:20 -03:00
|
|
|
# Column: Current instruction indicator
|
|
|
|
if mark_as_current:
|
|
|
|
fields.append('-->')
|
|
|
|
else:
|
|
|
|
fields.append(' ')
|
|
|
|
# Column: Opcode name
|
2017-04-19 14:36:31 -03:00
|
|
|
fields.append(self.opname.ljust(_OPNAME_WIDTH))
|
2013-05-06 10:59:20 -03:00
|
|
|
# Column: Opcode argument
|
|
|
|
if self.arg is not None:
|
2023-06-11 12:50:34 -03:00
|
|
|
arg = repr(self.arg)
|
|
|
|
# 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.
|
|
|
|
opname_excess = max(0, len(self.opname) - _OPNAME_WIDTH)
|
|
|
|
fields.append(repr(self.arg).rjust(_OPARG_WIDTH - opname_excess))
|
2013-05-06 10:59:20 -03:00
|
|
|
# Column: Opcode argument details
|
|
|
|
if self.argrepr:
|
|
|
|
fields.append('(' + self.argrepr + ')')
|
2013-11-06 08:08:36 -04:00
|
|
|
return ' '.join(fields).rstrip()
|
2013-05-06 10:59:20 -03:00
|
|
|
|
2023-06-11 12:50:34 -03:00
|
|
|
def __str__(self):
|
|
|
|
return self._disassemble()
|
2013-05-06 10:59:20 -03:00
|
|
|
|
2022-04-19 05:45:08 -03:00
|
|
|
def get_instructions(x, *, first_line=None, show_caches=False, adaptive=False):
|
2013-05-06 10:59:20 -03:00
|
|
|
"""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.
|
|
|
|
|
2013-11-06 08:08:36 -04:00
|
|
|
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.
|
2013-05-06 10:59:20 -03:00
|
|
|
"""
|
|
|
|
co = _get_code_object(x)
|
2003-10-28 08:17:25 -04:00
|
|
|
linestarts = dict(findlinestarts(co))
|
2013-11-06 08:08:36 -04:00
|
|
|
if first_line is not None:
|
|
|
|
line_offset = first_line - co.co_firstlineno
|
|
|
|
else:
|
|
|
|
line_offset = 0
|
2022-04-19 05:45:08 -03:00
|
|
|
return _get_instructions_bytes(_get_code_array(co, adaptive),
|
2021-06-07 15:22:26 -03:00
|
|
|
co._varname_from_oparg,
|
|
|
|
co.co_names, co.co_consts,
|
2022-02-25 08:11:34 -04:00
|
|
|
linestarts, line_offset,
|
|
|
|
co_positions=co.co_positions(),
|
2023-07-20 13:35:39 -03:00
|
|
|
show_caches=show_caches,
|
|
|
|
original_code=co.co_code)
|
2013-05-06 10:59:20 -03:00
|
|
|
|
2021-09-15 06:14:15 -03:00
|
|
|
def _get_const_value(op, arg, co_consts):
|
|
|
|
"""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.
|
|
|
|
"""
|
|
|
|
assert op in hasconst
|
|
|
|
|
|
|
|
argval = UNKNOWN
|
2023-04-26 15:00:36 -03:00
|
|
|
if co_consts is not None:
|
|
|
|
argval = co_consts[arg]
|
2021-09-15 06:14:15 -03:00
|
|
|
return argval
|
|
|
|
|
|
|
|
def _get_const_info(op, arg, co_consts):
|
2013-05-06 10:59:20 -03:00
|
|
|
"""Helper to get optional details about const references
|
|
|
|
|
2021-09-15 06:14:15 -03:00
|
|
|
Returns the dereferenced constant and its repr if the value
|
|
|
|
can be calculated.
|
2021-09-14 06:09:05 -03:00
|
|
|
Otherwise returns the sentinel value dis.UNKNOWN for the value
|
|
|
|
and an empty string for its repr.
|
2013-05-06 10:59:20 -03:00
|
|
|
"""
|
2021-09-15 06:14:15 -03:00
|
|
|
argval = _get_const_value(op, arg, co_consts)
|
|
|
|
argrepr = repr(argval) if argval is not UNKNOWN else ''
|
|
|
|
return argval, argrepr
|
2013-05-06 10:59:20 -03:00
|
|
|
|
2021-06-07 15:22:26 -03:00
|
|
|
def _get_name_info(name_index, get_name, **extrainfo):
|
2013-05-06 10:59:20 -03:00
|
|
|
"""Helper to get optional details about named references
|
|
|
|
|
|
|
|
Returns the dereferenced name as both value and repr if the name
|
|
|
|
list is defined.
|
2021-09-14 06:09:05 -03:00
|
|
|
Otherwise returns the sentinel value dis.UNKNOWN for the value
|
|
|
|
and an empty string for its repr.
|
2013-05-06 10:59:20 -03:00
|
|
|
"""
|
2021-06-07 15:22:26 -03:00
|
|
|
if get_name is not None:
|
|
|
|
argval = get_name(name_index, **extrainfo)
|
2021-09-14 06:09:05 -03:00
|
|
|
return argval, argval
|
2013-05-06 10:59:20 -03:00
|
|
|
else:
|
2021-09-14 06:09:05 -03:00
|
|
|
return UNKNOWN, ''
|
2013-05-06 10:59:20 -03:00
|
|
|
|
2022-08-14 11:42:31 -03:00
|
|
|
def _parse_varint(iterator):
|
2021-05-07 11:19:19 -03:00
|
|
|
b = next(iterator)
|
|
|
|
val = b & 63
|
|
|
|
while b&64:
|
|
|
|
val <<= 6
|
|
|
|
b = next(iterator)
|
|
|
|
val |= b&63
|
|
|
|
return val
|
|
|
|
|
2022-08-14 11:42:31 -03:00
|
|
|
def _parse_exception_table(code):
|
2021-05-07 11:19:19 -03:00
|
|
|
iterator = iter(code.co_exceptiontable)
|
|
|
|
entries = []
|
|
|
|
try:
|
|
|
|
while True:
|
2022-08-14 11:42:31 -03:00
|
|
|
start = _parse_varint(iterator)*2
|
|
|
|
length = _parse_varint(iterator)*2
|
2021-05-07 11:19:19 -03:00
|
|
|
end = start + length
|
2022-08-14 11:42:31 -03:00
|
|
|
target = _parse_varint(iterator)*2
|
|
|
|
dl = _parse_varint(iterator)
|
2021-05-07 11:19:19 -03:00
|
|
|
depth = dl >> 1
|
|
|
|
lasti = bool(dl&1)
|
|
|
|
entries.append(_ExceptionTableEntry(start, end, target, depth, lasti))
|
|
|
|
except StopIteration:
|
|
|
|
return entries
|
|
|
|
|
2022-04-11 06:40:24 -03:00
|
|
|
def _is_backward_jump(op):
|
2023-11-24 14:13:25 -04:00
|
|
|
return opname[op] in ('JUMP_BACKWARD',
|
|
|
|
'JUMP_BACKWARD_NO_INTERRUPT',
|
|
|
|
'ENTER_EXECUTOR')
|
2022-04-11 06:40:24 -03:00
|
|
|
|
2021-06-07 15:22:26 -03:00
|
|
|
def _get_instructions_bytes(code, varname_from_oparg=None,
|
2021-09-15 06:14:15 -03:00
|
|
|
names=None, co_consts=None,
|
2021-06-07 15:22:26 -03:00
|
|
|
linestarts=None, line_offset=0,
|
2022-02-25 08:11:34 -04:00
|
|
|
exception_entries=(), co_positions=None,
|
2023-07-20 13:35:39 -03:00
|
|
|
show_caches=False, original_code=None):
|
2013-05-06 10:59:20 -03:00
|
|
|
"""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
|
2021-09-15 06:14:15 -03:00
|
|
|
(e.g. variable names, co_consts) can be specified using optional
|
2013-05-06 10:59:20 -03:00
|
|
|
arguments.
|
|
|
|
|
|
|
|
"""
|
2023-07-20 13:35:39 -03:00
|
|
|
# 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:
|
|
|
|
original_code = original_code or code
|
2021-07-04 16:05:05 -03:00
|
|
|
co_positions = co_positions or iter(())
|
2021-06-07 15:22:26 -03:00
|
|
|
get_name = None if names is None else names.__getitem__
|
2023-11-22 18:36:55 -04:00
|
|
|
|
|
|
|
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)
|
2021-05-07 11:19:19 -03:00
|
|
|
labels.add(target)
|
2023-11-22 18:36:55 -04:00
|
|
|
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)
|
2023-12-03 08:12:49 -04:00
|
|
|
label_width = 4 + len(str(len(labels_map)))
|
2023-11-22 18:36:55 -04:00
|
|
|
|
|
|
|
exceptions_map = {}
|
|
|
|
for start, end, target, _, _ in exception_entries:
|
|
|
|
exceptions_map[start] = labels_map[target]
|
|
|
|
exceptions_map[end] = -1
|
|
|
|
|
2023-08-25 05:31:26 -03:00
|
|
|
starts_line = False
|
|
|
|
local_line_number = None
|
|
|
|
line_number = None
|
2023-07-20 13:35:39 -03:00
|
|
|
for offset, start_offset, op, arg in _unpack_opargs(original_code):
|
2013-05-06 10:59:20 -03:00
|
|
|
if linestarts is not None:
|
2023-08-25 05:31:26 -03:00
|
|
|
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
|
2022-01-24 07:09:20 -04:00
|
|
|
positions = Positions(*next(co_positions, ()))
|
2022-04-19 05:45:08 -03:00
|
|
|
deop = _deoptop(op)
|
2023-07-20 13:35:39 -03:00
|
|
|
op = code[offset]
|
2023-11-12 10:06:02 -04:00
|
|
|
|
|
|
|
yield Instruction._create(op, arg, offset, start_offset, starts_line, line_number,
|
2023-11-22 18:36:55 -04:00
|
|
|
positions, co_consts=co_consts,
|
|
|
|
varname_from_oparg=varname_from_oparg, names=names,
|
|
|
|
labels_map=labels_map, exceptions_map=exceptions_map)
|
2023-11-12 10:06:02 -04:00
|
|
|
|
|
|
|
caches = _get_cache_size(_all_opname[deop])
|
2022-06-16 17:49:32 -03:00
|
|
|
if not caches:
|
|
|
|
continue
|
|
|
|
if not show_caches:
|
|
|
|
# We still need to advance the co_positions iterator:
|
|
|
|
for _ in range(caches):
|
|
|
|
next(co_positions, ())
|
|
|
|
continue
|
|
|
|
for name, size in _cache_format[opname[deop]].items():
|
|
|
|
for i in range(size):
|
|
|
|
offset += 2
|
|
|
|
# Only show the fancy argrepr for a CACHE instruction when it's
|
2022-11-09 14:50:09 -04:00
|
|
|
# the first entry for a particular cache value:
|
|
|
|
if i == 0:
|
2022-06-16 17:49:32 -03:00
|
|
|
data = code[offset: offset + 2 * size]
|
|
|
|
argrepr = f"{name}: {int.from_bytes(data, sys.byteorder)}"
|
|
|
|
else:
|
2022-05-06 11:18:09 -03:00
|
|
|
argrepr = ""
|
2022-06-16 17:49:32 -03:00
|
|
|
yield Instruction(
|
2023-11-22 18:36:55 -04:00
|
|
|
"CACHE", CACHE, 0, None, argrepr, offset, offset, False, None, None,
|
2022-06-16 17:49:32 -03:00
|
|
|
Positions(*next(co_positions, ()))
|
|
|
|
)
|
2013-05-06 10:59:20 -03:00
|
|
|
|
2023-11-22 18:36:55 -04:00
|
|
|
def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False,
|
|
|
|
show_offsets=False):
|
2013-05-06 10:59:20 -03:00
|
|
|
"""Disassemble a code object."""
|
|
|
|
linestarts = dict(findlinestarts(co))
|
2022-08-14 11:42:31 -03:00
|
|
|
exception_entries = _parse_exception_table(co)
|
2022-04-19 05:45:08 -03:00
|
|
|
_disassemble_bytes(_get_code_array(co, adaptive),
|
|
|
|
lasti, co._varname_from_oparg,
|
2021-06-07 15:22:26 -03:00
|
|
|
co.co_names, co.co_consts, linestarts, file=file,
|
2022-02-25 08:11:34 -04:00
|
|
|
exception_entries=exception_entries,
|
2023-07-20 13:35:39 -03:00
|
|
|
co_positions=co.co_positions(), show_caches=show_caches,
|
2023-11-22 18:36:55 -04:00
|
|
|
original_code=co.co_code, show_offsets=show_offsets)
|
2001-01-14 19:36:06 -04:00
|
|
|
|
2023-11-22 18:36:55 -04:00
|
|
|
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)
|
2017-06-11 08:09:39 -03:00
|
|
|
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)
|
2022-02-25 08:11:34 -04:00
|
|
|
_disassemble_recursive(
|
2023-11-22 18:36:55 -04:00
|
|
|
x, file=file, depth=depth, show_caches=show_caches,
|
|
|
|
adaptive=adaptive, show_offsets=show_offsets
|
2022-02-25 08:11:34 -04:00
|
|
|
)
|
2017-06-11 08:09:39 -03:00
|
|
|
|
2021-06-07 15:22:26 -03:00
|
|
|
def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
|
2021-09-15 06:14:15 -03:00
|
|
|
names=None, co_consts=None, linestarts=None,
|
2021-07-04 16:05:05 -03:00
|
|
|
*, file=None, line_offset=0, exception_entries=(),
|
2023-11-22 18:36:55 -04:00
|
|
|
co_positions=None, show_caches=False, original_code=None,
|
|
|
|
show_offsets=False):
|
2013-05-06 10:59:20 -03:00
|
|
|
# Omit the line number column entirely if we have no line number info
|
2023-08-25 05:31:26 -03:00
|
|
|
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
|
|
|
|
|
2017-04-19 14:36:31 -03:00
|
|
|
if show_lineno:
|
2023-08-25 05:31:26 -03:00
|
|
|
maxlineno = max(linestarts_ints) + line_offset
|
2017-04-19 14:36:31 -03:00
|
|
|
if maxlineno >= 1000:
|
|
|
|
lineno_width = len(str(maxlineno))
|
|
|
|
else:
|
|
|
|
lineno_width = 3
|
2023-08-25 05:31:26 -03:00
|
|
|
|
|
|
|
if lineno_width < len(str(None)) and None in linestarts.values():
|
|
|
|
lineno_width = len(str(None))
|
2017-04-19 14:36:31 -03:00
|
|
|
else:
|
|
|
|
lineno_width = 0
|
2023-11-22 18:36:55 -04:00
|
|
|
if show_offsets:
|
|
|
|
maxoffset = len(code) - 2
|
|
|
|
if maxoffset >= 10000:
|
|
|
|
offset_width = len(str(maxoffset))
|
|
|
|
else:
|
|
|
|
offset_width = 4
|
2017-04-19 14:36:31 -03:00
|
|
|
else:
|
2023-11-22 18:36:55 -04:00
|
|
|
offset_width = 0
|
|
|
|
|
2023-12-03 08:12:49 -04:00
|
|
|
label_width = -1
|
2021-06-07 15:22:26 -03:00
|
|
|
for instr in _get_instructions_bytes(code, varname_from_oparg, names,
|
2021-09-15 06:14:15 -03:00
|
|
|
co_consts, linestarts,
|
2022-02-25 08:11:34 -04:00
|
|
|
line_offset=line_offset,
|
|
|
|
exception_entries=exception_entries,
|
|
|
|
co_positions=co_positions,
|
2023-07-20 13:35:39 -03:00
|
|
|
show_caches=show_caches,
|
|
|
|
original_code=original_code):
|
2013-05-06 10:59:20 -03:00
|
|
|
new_source_line = (show_lineno and
|
2023-08-25 05:31:26 -03:00
|
|
|
instr.starts_line and
|
2013-05-06 10:59:20 -03:00
|
|
|
instr.offset > 0)
|
|
|
|
if new_source_line:
|
|
|
|
print(file=file)
|
2023-03-27 19:22:06 -03:00
|
|
|
if show_caches:
|
|
|
|
is_current_instr = instr.offset == lasti
|
|
|
|
else:
|
|
|
|
# Each CACHE takes 2 bytes
|
|
|
|
is_current_instr = instr.offset <= lasti \
|
2023-07-27 10:15:25 -03:00
|
|
|
<= instr.offset + 2 * _get_cache_size(_all_opname[_deoptop(instr.opcode)])
|
2023-12-03 08:12:49 -04:00
|
|
|
label_width = getattr(instr, 'label_width', label_width)
|
|
|
|
assert label_width >= 0
|
|
|
|
print(instr._disassemble(lineno_width, is_current_instr, offset_width, label_width),
|
2017-04-19 14:36:31 -03:00
|
|
|
file=file)
|
2021-05-07 11:19:19 -03:00
|
|
|
if exception_entries:
|
|
|
|
print("ExceptionTable:", file=file)
|
|
|
|
for entry in exception_entries:
|
|
|
|
lasti = " lasti" if entry.lasti else ""
|
2023-11-22 18:36:55 -04:00
|
|
|
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)
|
2003-02-27 17:29:27 -04:00
|
|
|
|
2017-06-11 08:09:39 -03:00
|
|
|
def _disassemble_str(source, **kwargs):
|
2010-07-03 04:36:51 -03:00
|
|
|
"""Compile the source string, then disassemble the code object."""
|
2017-06-11 08:09:39 -03:00
|
|
|
_disassemble_recursive(_try_compile(source, '<dis>'), **kwargs)
|
2010-07-03 04:36:51 -03:00
|
|
|
|
2001-01-14 19:36:06 -04:00
|
|
|
disco = disassemble # XXX For backwards compatibility
|
1997-01-17 16:05:04 -04:00
|
|
|
|
2022-02-18 05:56:23 -04:00
|
|
|
|
|
|
|
# Rely on C `int` being 32 bits for oparg
|
|
|
|
_INT_BITS = 32
|
|
|
|
# Value for c int when it overflows
|
|
|
|
_INT_OVERFLOW = 2 ** (_INT_BITS - 1)
|
|
|
|
|
2016-05-08 17:43:50 -03:00
|
|
|
def _unpack_opargs(code):
|
|
|
|
extended_arg = 0
|
2023-06-11 12:50:34 -03:00
|
|
|
extended_args_offset = 0 # Number of EXTENDED_ARG instructions preceding the current instruction
|
2022-05-06 14:57:08 -03:00
|
|
|
caches = 0
|
2016-05-24 03:15:14 -03:00
|
|
|
for i in range(0, len(code), 2):
|
2022-05-06 14:57:08 -03:00
|
|
|
# Skip inline CACHE entries:
|
|
|
|
if caches:
|
|
|
|
caches -= 1
|
|
|
|
continue
|
2007-10-19 19:06:24 -03:00
|
|
|
op = code[i]
|
2022-05-06 14:57:08 -03:00
|
|
|
deop = _deoptop(op)
|
2023-07-27 10:15:25 -03:00
|
|
|
caches = _get_cache_size(_all_opname[deop])
|
2022-07-01 11:33:35 -03:00
|
|
|
if deop in hasarg:
|
2016-05-24 03:15:14 -03:00
|
|
|
arg = code[i+1] | extended_arg
|
2022-06-02 23:29:27 -03:00
|
|
|
extended_arg = (arg << 8) if deop == EXTENDED_ARG else 0
|
2022-02-18 05:56:23 -04:00
|
|
|
# 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 extended_arg >= _INT_OVERFLOW:
|
|
|
|
extended_arg -= 2 * _INT_OVERFLOW
|
2016-05-24 03:15:14 -03:00
|
|
|
else:
|
|
|
|
arg = None
|
2021-11-09 16:07:38 -04:00
|
|
|
extended_arg = 0
|
2023-06-11 12:50:34 -03:00
|
|
|
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
|
2016-05-08 17:43:50 -03:00
|
|
|
|
|
|
|
def findlabels(code):
|
|
|
|
"""Detect all offsets in a byte code which are jump targets.
|
|
|
|
|
|
|
|
Return the list of offsets.
|
|
|
|
|
|
|
|
"""
|
|
|
|
labels = []
|
2023-06-11 12:50:34 -03:00
|
|
|
for offset, _, op, arg in _unpack_opargs(code):
|
2016-05-08 17:43:50 -03:00
|
|
|
if arg is not None:
|
2023-06-11 12:50:34 -03:00
|
|
|
label = _get_jump_target(op, arg, offset)
|
|
|
|
if label is None:
|
2016-05-24 03:15:14 -03:00
|
|
|
continue
|
|
|
|
if label not in labels:
|
|
|
|
labels.append(label)
|
2001-01-14 19:36:06 -04:00
|
|
|
return labels
|
1990-12-26 11:40:07 -04:00
|
|
|
|
2003-10-28 08:17:25 -04:00
|
|
|
def findlinestarts(code):
|
|
|
|
"""Find the offsets in a byte code which are start of lines in the source.
|
|
|
|
|
2020-11-12 05:43:29 -04:00
|
|
|
Generate pairs (offset, lineno)
|
2023-08-25 05:31:26 -03:00
|
|
|
lineno will be an integer or None the offset does not have a source line.
|
2003-10-28 08:17:25 -04:00
|
|
|
"""
|
2023-08-25 05:31:26 -03:00
|
|
|
|
|
|
|
lastline = False # None is a valid line number
|
2020-11-12 05:43:29 -04:00
|
|
|
for start, end, line in code.co_lines():
|
2023-08-25 05:31:26 -03:00
|
|
|
if line is not lastline:
|
2020-11-12 05:43:29 -04:00
|
|
|
lastline = line
|
|
|
|
yield start, line
|
|
|
|
return
|
|
|
|
|
2021-09-09 10:04:12 -03:00
|
|
|
def _find_imports(co):
|
|
|
|
"""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__.
|
|
|
|
"""
|
|
|
|
IMPORT_NAME = opmap['IMPORT_NAME']
|
|
|
|
|
|
|
|
consts = co.co_consts
|
|
|
|
names = co.co_names
|
2023-06-11 12:50:34 -03:00
|
|
|
opargs = [(op, arg) for _, _, op, arg in _unpack_opargs(co.co_code)
|
2021-09-09 10:04:12 -03:00
|
|
|
if op != EXTENDED_ARG]
|
|
|
|
for i, (op, oparg) in enumerate(opargs):
|
2021-09-15 06:14:15 -03:00
|
|
|
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)
|
2021-09-09 10:04:12 -03:00
|
|
|
|
|
|
|
def _find_store_names(co):
|
|
|
|
"""Find names of variables which are written in the code
|
|
|
|
|
|
|
|
Generate sequence of strings
|
|
|
|
"""
|
|
|
|
STORE_OPS = {
|
|
|
|
opmap['STORE_NAME'],
|
|
|
|
opmap['STORE_GLOBAL']
|
|
|
|
}
|
|
|
|
|
|
|
|
names = co.co_names
|
2023-06-11 12:50:34 -03:00
|
|
|
for _, _, op, arg in _unpack_opargs(co.co_code):
|
2021-09-09 10:04:12 -03:00
|
|
|
if op in STORE_OPS:
|
|
|
|
yield names[arg]
|
|
|
|
|
2000-02-04 13:47:55 -04:00
|
|
|
|
2013-05-06 10:59:20 -03:00
|
|
|
class Bytecode:
|
|
|
|
"""The bytecode operations of a piece of code
|
|
|
|
|
2017-08-17 23:29:21 -03:00
|
|
|
Instantiate this with a function, method, other compiled object, string of
|
|
|
|
code, or a code object (as returned by compile()).
|
2013-05-06 10:59:20 -03:00
|
|
|
|
|
|
|
Iterating over this yields the bytecode operations as Instruction instances.
|
|
|
|
"""
|
2023-11-22 18:36:55 -04:00
|
|
|
def __init__(self, x, *, first_line=None, current_offset=None, show_caches=False, adaptive=False, show_offsets=False):
|
2013-11-06 08:08:36 -04:00
|
|
|
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
|
2013-11-22 10:57:00 -04:00
|
|
|
self.current_offset = current_offset
|
2022-08-14 11:42:31 -03:00
|
|
|
self.exception_entries = _parse_exception_table(co)
|
2022-02-25 08:11:34 -04:00
|
|
|
self.show_caches = show_caches
|
2022-04-19 05:45:08 -03:00
|
|
|
self.adaptive = adaptive
|
2023-11-22 18:36:55 -04:00
|
|
|
self.show_offsets = show_offsets
|
2013-05-06 10:59:20 -03:00
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
co = self.codeobj
|
2022-04-19 05:45:08 -03:00
|
|
|
return _get_instructions_bytes(_get_code_array(co, self.adaptive),
|
2021-06-07 15:22:26 -03:00
|
|
|
co._varname_from_oparg,
|
|
|
|
co.co_names, co.co_consts,
|
2013-11-06 08:08:36 -04:00
|
|
|
self._linestarts,
|
2021-05-07 11:19:19 -03:00
|
|
|
line_offset=self._line_offset,
|
2021-09-03 12:29:09 -03:00
|
|
|
exception_entries=self.exception_entries,
|
2022-02-25 08:11:34 -04:00
|
|
|
co_positions=co.co_positions(),
|
2023-07-20 13:35:39 -03:00
|
|
|
show_caches=self.show_caches,
|
|
|
|
original_code=co.co_code)
|
2013-05-06 10:59:20 -03:00
|
|
|
|
|
|
|
def __repr__(self):
|
2013-11-06 08:08:36 -04:00
|
|
|
return "{}({!r})".format(self.__class__.__name__,
|
|
|
|
self._original_object)
|
2013-05-06 10:59:20 -03:00
|
|
|
|
2013-11-22 10:57:00 -04:00
|
|
|
@classmethod
|
2022-04-19 05:45:08 -03:00
|
|
|
def from_traceback(cls, tb, *, show_caches=False, adaptive=False):
|
2013-11-22 10:57:00 -04:00
|
|
|
""" Construct a Bytecode from the given traceback """
|
|
|
|
while tb.tb_next:
|
|
|
|
tb = tb.tb_next
|
2022-02-25 08:11:34 -04:00
|
|
|
return cls(
|
2022-04-19 05:45:08 -03:00
|
|
|
tb.tb_frame.f_code, current_offset=tb.tb_lasti, show_caches=show_caches, adaptive=adaptive
|
2022-02-25 08:11:34 -04:00
|
|
|
)
|
2013-11-22 10:57:00 -04:00
|
|
|
|
2013-05-06 10:59:20 -03:00
|
|
|
def info(self):
|
|
|
|
"""Return formatted information about the code object."""
|
|
|
|
return _format_code_info(self.codeobj)
|
|
|
|
|
2013-11-06 08:08:36 -04:00
|
|
|
def dis(self):
|
|
|
|
"""Return a formatted view of the bytecode operations."""
|
2013-05-06 10:59:20 -03:00
|
|
|
co = self.codeobj
|
2013-11-22 10:57:00 -04:00
|
|
|
if self.current_offset is not None:
|
|
|
|
offset = self.current_offset
|
|
|
|
else:
|
|
|
|
offset = -1
|
2013-11-06 08:08:36 -04:00
|
|
|
with io.StringIO() as output:
|
2022-04-19 05:45:08 -03:00
|
|
|
_disassemble_bytes(_get_code_array(co, self.adaptive),
|
2021-06-07 15:22:26 -03:00
|
|
|
varname_from_oparg=co._varname_from_oparg,
|
2021-09-15 06:14:15 -03:00
|
|
|
names=co.co_names, co_consts=co.co_consts,
|
2013-11-06 08:08:36 -04:00
|
|
|
linestarts=self._linestarts,
|
|
|
|
line_offset=self._line_offset,
|
2013-11-22 10:57:00 -04:00
|
|
|
file=output,
|
2021-05-07 11:19:19 -03:00
|
|
|
lasti=offset,
|
2021-07-04 16:05:05 -03:00
|
|
|
exception_entries=self.exception_entries,
|
2022-02-25 08:11:34 -04:00
|
|
|
co_positions=co.co_positions(),
|
2023-07-20 13:35:39 -03:00
|
|
|
show_caches=self.show_caches,
|
2023-11-22 18:36:55 -04:00
|
|
|
original_code=co.co_code,
|
|
|
|
show_offsets=self.show_offsets)
|
2013-11-06 08:08:36 -04:00
|
|
|
return output.getvalue()
|
2013-05-06 10:59:20 -03:00
|
|
|
|
|
|
|
|
2023-10-10 19:31:28 -03:00
|
|
|
def main():
|
2013-08-24 11:48:17 -03:00
|
|
|
import argparse
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser()
|
2023-10-02 21:49:34 -03:00
|
|
|
parser.add_argument('-C', '--show-caches', action='store_true',
|
|
|
|
help='show inline caches')
|
2023-11-22 18:36:55 -04:00
|
|
|
parser.add_argument('-O', '--show-offsets', action='store_true',
|
|
|
|
help='show instruction offsets')
|
2020-08-08 00:03:09 -03:00
|
|
|
parser.add_argument('infile', type=argparse.FileType('rb'), nargs='?', default='-')
|
2013-08-24 11:48:17 -03:00
|
|
|
args = parser.parse_args()
|
|
|
|
with args.infile as infile:
|
|
|
|
source = infile.read()
|
|
|
|
code = compile(source, args.infile.name, "exec")
|
2023-11-22 18:36:55 -04:00
|
|
|
dis(code, show_caches=args.show_caches, show_offsets=args.show_offsets)
|
2000-02-04 13:47:55 -04:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2023-10-10 19:31:28 -03:00
|
|
|
main()
|