dis —- Python 字节码反汇编器
Source code:Lib/dis.py
dis
模块通过反汇编支持CPython的 bytecode 分析。该模块作为输入的 CPython 字节码在文件 Include/opcode.h
中定义,并由编译器和解释器使用。
CPython implementation detail: 字节码是 CPython 解释器的实现细节。不保证不会在Python版本之间添加、删除或更改字节码。不应考虑将此模块的跨 Python VM 或 Python 版本的使用。
在 3.6 版更改: 每条指令使用2个字节。以前字节数因指令而异。
示例:给出函数 myfunc()
:
- def myfunc(alist):
- return len(alist)
可以使用以下命令显示 myfunc()
的反汇编
- >>> dis.dis(myfunc)
- 2 0 LOAD_GLOBAL 0 (len)
- 2 LOAD_FAST 0 (alist)
- 4 CALL_FUNCTION 1
- 6 RETURN_VALUE
("2" 是行号)。
字节码分析
3.4 新版功能.
字节码分析 API 允许将 Python 代码片段包装在 Bytecode
对象中,以便轻松访问已编译代码的详细信息。
- class
dis.
Bytecode
(x, *, first_line=None, current_offset=None) - 分析的字节码对应于函数、生成器、异步生成器、协程、方法、源代码字符串或代码对象(由
compile()
返回)。
这是下面列出的许多函数的便利包装,最值得注意的是 get_instructions()
,迭代于 Bytecode
的实例产生字节码操作 Instruction
的实例。
如果 first_line 不是 None
,则表示应该为反汇编代码中的第一个源代码行报告的行号。否则,源行信息(如果有的话)直接来自反汇编的代码对象。
如果 current_offset 不是 None
,则它指的是反汇编代码中的指令偏移量。设置它意味着 dis()
将针对指定的操作码显示“当前指令”标记。
- classmethod
fromtraceback
(_tb) 从给定回溯构造一个
Bytecode
实例,设置 current_offset 为异常负责的指令。已编译的代码对象。
代码对象的第一个源代码行(如果可用)
返回字节码操作的格式化视图(与
dis.dis()
打印相同,但作为多行字符串返回)。- 返回带有关于代码对象的详细信息的格式化多行字符串,如
code_info()
。
在 3.7 版更改: 现在可以处理协程和异步生成器对象。
示例:
- >>> bytecode = dis.Bytecode(myfunc)
- >>> for instr in bytecode:
- ... print(instr.opname)
- ...
- LOAD_GLOBAL
- LOAD_FAST
- CALL_FUNCTION
- RETURN_VALUE
分析函数
dis
模块还定义了以下分析函数,它们将输入直接转换为所需的输出。如果只执行单个操作,它们可能很有用,因此中间分析对象没用:
请注意,代码信息字符串的确切内容是高度依赖于实现的,它们可能会在Python VM或Python版本中任意更改。
3.2 新版功能.
在 3.7 版更改: 现在可以处理协程和异步生成器对象。
这是 print(code_info(x), file= file)
的便捷简写,用于在解释器提示符下进行交互式探索。
3.2 新版功能.
在 3.4 版更改: 添加 file 参数。
dis.
dis
(x=None, *, file=None, depth=None)- 反汇编 x 对象。 x 可以表示模块、类、方法、函数、生成器、异步生成器、协程、代码对象、源代码字符串或原始字节码的字节序列。对于模块,它会反汇编所有功能。对于一个类,它反汇编所有方法(包括类和静态方法)。对于代码对象或原始字节码序列,它每字节码指令打印一行。它还递归地反汇编嵌套代码对象(推导式代码,生成器表达式和嵌套函数,以及用于构建嵌套类的代码)。在被反汇编之前,首先使用
compile()
内置函数将字符串编译为代码对象。如果未提供任何对象,则此函数会反汇编最后一次回溯。
如果提供的话,反汇编将作为文本写入提供的 file 参数,否则写入 sys.stdout
。
递归的最大深度受 depth 限制,除非它是 None
。 depth=0
表示没有递归。
在 3.4 版更改: 添加 file 参数。
在 3.7 版更改: 实现了递归反汇编并添加了 depth 参数。
在 3.7 版更改: 现在可以处理协程和异步生成器对象。
如果提供的话,反汇编将作为文本写入提供的 file 参数,否则写入 sys.stdout
。
在 3.4 版更改: 添加 file 参数。
dis.
disassemble
(code, lasti=-1, *, file=None)dis.
disco
(code, lasti=-1, *, file=None)反汇编代码对象,如果提供了 lasti ,则指示最后一条指令。输出分为以下几列:
行号,用于每行的第一条指令
当前指令,表示为
—>
,一个标记的指令,用
>>
表示,指令的地址,
操作码名称,
操作参数,和
括号中的参数解释。
参数解释识别本地和全局变量名称、常量值、分支目标和比较运算符。
如果提供的话,反汇编将作为文本写入提供的 file 参数,否则写入 sys.stdout
。
在 3.4 版更改: 添加 file 参数。
迭代器生成一系列 Instruction
,命名为元组,提供所提供代码中每个操作的详细信息。
如果 first_line 不是 None
,则表示应该为反汇编代码中的第一个源代码行报告的行号。否则,源行信息(如果有的话)直接来自反汇编的代码对象。
3.4 新版功能.
dis.
findlinestarts
(code)- 此生成器函数使用代码对象 code 的
co_firstlineno
和co_lnotab
属性来查找源代码中行开头的偏移量。它们生成为(offset, lineno)
格式以及如何解码它。`对。请参阅 :source:
objects/lnotab_notes.txt` ,了解 ``co_lnotab
在 3.6 版更改: 行号可能会减少。 以前,他们总是在增加。
3.4 新版功能.
Python字节码说明
get_instructions()
函数和 Bytecode
类提供字节码指令的详细信息的 Instruction
实例:
- class
dis.
Instruction
字节码操作的详细信息
opcode
操作的数字代码,对应于下面列出的操作码值和 操作码集合 中的字节码值。
人类可读的操作名称
操作的数字参数(如果有的话),否则为
None
已解析的 arg 值(如果已知),否则与 arg 相同
人类可读的操作参数描述
在字节码序列中启动操作索引
行由此操作码(如果有)启动,否则为
None
- 如果其他代码跳到这里,则为
True
,否则为False
3.4 新版功能.
Python编译器当前生成以下字节码指令。
一般指令
3.2 新版功能.
3.2 新版功能.
一元操作
一元操作获取堆栈顶部元素,应用操作,并将结果推回堆栈。
GET_YIELD_FROM_ITER
- 如果
TOS
是一个 generator iterator 或 coroutine 对象则保持原样。否则实现TOS = iter(TOS)
。
3.5 新版功能.
二元操作
二元操作从堆栈中删除堆栈顶部(TOS)和第二个最顶层堆栈项(TOS1)。 它们执行操作,并将结果放回堆栈。
3.5 新版功能.
就地操作
就地操作就像二元操作,因为它们删除了TOS和TOS1,并将结果推回到堆栈上,但是当TOS1支持它时,操作就地完成,并且产生的TOS可能是(但不一定) 原来的TOS1。
3.5 新版功能.
协程操作码
GET_AWAITABLE
- 实现
TOS = getawaitable(TOS)
,其中getawaitable(o)
返回o
如果o
是一个有 CO_ITERABLE_COROUTINE 标志的协程对象或生成器对象,否则解析o.__await
。
3.5 新版功能.
3.5 新版功能.
在 3.7 版更改: 已经不再支持从 aiter
返回可等待对象。
3.5 新版功能.
3.5 新版功能.
3.5 新版功能.
其他操作码
PRINT_EXPR
- 实现交互模式的表达式语句。TOS从堆栈中被移除并打印。在非交互模式下,表达式语句以
POP_TOP
终止。
BREAK_LOOP
- Terminates a loop due to a
break
statement.
CONTINUELOOP
(_target)- Continues a loop due to a
continue
statement. target is theaddress to jump to (which should be aFOR_ITER
instruction).
3.1 新版功能.
对于所有 SET_ADD
、 LIST_APPEND
和 MAP_ADD
指令,当弹出添加的值或键值对时,容器对象保留在堆栈上,以便它可用于循环的进一步迭代。
YIELD_VALUE
- 弹出 TOS 并从一个 generator 生成它。
YIELD_FROM
- 弹出 TOS 并将其委托给它作为 generator 的子迭代器。
3.3 新版功能.
SETUP_ANNOTATIONS
- 检查
annotations
是否在locals()
中定义,如果没有,它被设置为空dict
。只有在类或模块体静态地包含 variable annotations 时才会发出此操作码。
3.6 新版功能.
POP_BLOCK
- Removes one block from the block stack. Per frame, there is a stack ofblocks, denoting nested loops, try statements, and such.
END_FINALLY
- Terminates a
finally
clause. The interpreter recalls whether theexception has to be re-raised, or whether the function returns, and continueswith the outer-next block.
LOAD_BUILD_CLASS
- 将
builtins . build_class()
推到堆栈上。它之后被CALL_FUNCTION
调用来构造一个类。
SETUPWITH
(_delta)- This opcode performs several operations before a with block starts. First,it loads
exit()
from the context manager and pushes it ontothe stack for later use byWITHCLEANUP
. Then,[enter()
](https://docs.python.org/zh-cn/3.7/reference/datamodel.html#object.__enter_) is called, and a finally block pointing to _delta_is pushed. Finally, the result of calling the enter method is pushed ontothe stack. The next opcode will either ignore it (POP_TOP
), orstore it in (a) variable(s) (STORE_FAST
,STORE_NAME
, orUNPACK_SEQUENCE
).
3.2 新版功能.
WITH_CLEANUP_START
Cleans up the stack when a
with
statement block exits. TOS is thecontext manager'sexit()
bound method. Below TOS are 1—3 valuesindicating how/why the finally clause was entered:SECOND =
None
(SECOND, THIRD) = (
WHY_{RETURN,CONTINUE}
), retvalSECOND =
WHY_*
; no retval below it(SECOND, THIRD, FOURTH) = exc_info()
In the last case, TOS(SECOND, THIRD, FOURTH)
is called, otherwiseTOS(None, None, None)
. Pushes SECOND and result of the callto the stack.
If the stack represents an exception, and the function call returns a'true' value, this information is "zapped" and replaced with a singleWHY_SILENCED
to prevent END_FINALLY
from re-raising theexception. (But non-local gotos will still be resumed.)
以下所有操作码均使用其参数。
STORENAME
(_namei)- 实现
name = TOS
。 namei 是 name 在代码对象的co_names
属性中的索引。 在可能的情况下,编译器会尝试使用STORE_FAST
或STORE_GLOBAL
。
counts 的低字节是列表值之前的值的数量,counts 中的高字节则是之后的值的数量。 结果值会按从右至左的顺序入栈。
STOREGLOBAL
(_namei)- 类似于
STORE_NAME
但会将 name 存储为全局变量。
DELETEGLOBAL
(_namei)- 类似于
DELETE_NAME
但会删除一个全局变量。
BUILDLIST
(_count)- 类似于
BUILD_TUPLE
但会创建一个列表。
BUILDSET
(_count)- 类似于
BUILD_TUPLE
但会创建一个集合。
在 3.5 版更改: 字典是根据栈中的项创建而不是创建一个预设大小包含 count 项的空字典。
BUILDCONST_KEY_MAP
(_count)- 专用于常量键的
BUILD_MAP
版本。 count 值是从栈中提取的。 栈顶的元素包含一个由键构成的元组。
3.6 新版功能.
3.6 新版功能.
3.5 新版功能.
BUILDTUPLE_UNPACK_WITH_CALL
(_count)- 这类似于
BUILD_TUPLE_UNPACK
但专用于f(x, y, *z)
调用语法。 栈中count + 1
位置上的项应当是相应的可调用对象f
。
3.6 新版功能.
BUILDLIST_UNPACK
(_count)- 这类似于
BUILD_TUPLE_UNPACK
但会将一个列表而非元组推入栈顶。 实现可迭代对象解包为列表形式[x, y, *z]
。
3.5 新版功能.
BUILDSET_UNPACK
(_count)- 这类似于
BUILD_TUPLE_UNPACK
但会将一个集合而非元组推入栈顶。 实现可迭代对象解包为集合形式{x, y, *z}
。
3.5 新版功能.
3.5 新版功能.
BUILDMAP_UNPACK_WITH_CALL
(_count)- 这类似于
BUILD_MAP_UNPACK
但专用于f(x, y, **z)
调用语法。 栈中count + 2
位置上的项应当是相应的可调用对象f
。
3.5 新版功能.
在 3.6 版更改: 可迭代对象的位置的确定方式是将操作码参数加 2 而不是将其编码到参数的第二个字节。
IMPORTNAME
(_namei)- 导入模块
conames[namei]
。 会弹出 TOS 和 TOS1 以提供 _fromlist 和 level 参数给import()
。 模块对象会被推入栈顶。 当前命名空间不受影响:对于一条标准 import 语句,会执行后续的STORE_FAST
指令来修改命名空间。
IMPORTFROM
(_namei)- 从在 TOS 内找到的模块中加载属性
co_names[namei]
。 结果对象会被推入栈顶,以便由后续的STORE_FAST
指令来保存。
3.1 新版功能.
3.1 新版功能.
3.1 新版功能.
3.1 新版功能.
FORITER
(_delta)- TOS 是一个 iterator。 可调用它的
next()
方法。 如果产生了一个新值,则将其推入栈顶(将迭代器留在其下方)。 如果迭代器提示已耗尽则 TOS 会被弹出,并将字节码计数器的值增加 delta。
SETUPLOOP
(_delta)- Pushes a block for a loop onto the block stack. The block spans from thecurrent instruction with a size of delta bytes.
SETUPEXCEPT
(_delta)- Pushes a try block from a try-except clause onto the block stack. _delta_points to the first except block.
SETUPFINALLY
(_delta)- Pushes a try block from a try-except clause onto the block stack. _delta_points to the finally block.
LOADCLOSURE
(_i)- 将一个包含在单元的第 i 个空位中的对单元的引用推入栈顶并释放可用的存储空间。 如果 i 小于 co_cellvars 的长度则变量的名称为
co_cellvars[i]
。 否则为co_freevars[i - len(co_cellvars)]
。
LOADCLASSDEREF
(_i)- 类似于
LOAD_DEREF
但在查询单元之前会首先检查局部对象字典。 这被用于加载类语句体中的自由变量。
3.4 新版功能.
DELETEDEREF
(_i)- 清空包含在单元的第 i 个空位中的单元并释放可用存储空间。 被用于
del
语句。
3.2 新版功能.
RAISEVARARGS
(_argc)使用
raise
语句的 3 种形式之一引发异常,具体形式取决于 argc 的值:0:
raise
(重新引发之前的异常)1:
raise TOS
(在TOS
上引发异常实例或类型)2:
raise TOS1 from TOS
(在TOS1
上引发异常实例或类型并将cause
设为TOS
)
CALLFUNCTION
(_argc)- 调用一个可调用对象并传入位置参数。 argc 指明位置参数的数量。 栈顶包含位置参数,其中最右边的参数在最顶端。 在参数之下是一个待调用的可调用对象。
CALL_FUNCTION
会从栈中弹出所有参数以及可调用对象,附带这些参数调用该可调用对象,并将可调用对象所返回的返回值推入栈顶。
在 3.6 版更改: 此操作码仅用于附带位置参数的调用。
CALLFUNCTION_KW
(_argc)- 调用一个可调用对象并传入位置参数(如果有的话)和关键字参数。 argc 指明位置参数和关键字参数的总数量。 栈顶元素包含一个关键字参数名称的元组。 在元组之下是根据元组排序的关键字参数。 在关键字参数之下是位置参数,其中最右边的参数在最顶端。 在参数之下是一个待调用的可调用对象。
CALL_FUNCTION_KW
会从栈中弹出所有参数以及可调用对象,附带这些参数调用该可调用对象,并将可调用对象所返回的返回值推入栈顶。
在 3.6 版更改: 关键字参数会被打包为一个元组而非字典,argc 指明参数的总数量。
CALLFUNCTION_EX
(_flags)- 调用一个可调用对象并附带位置参数和关键字参数变量集合。 如果设置了 flags 的最低位,则栈顶包含一个由额外关键字参数组成的映射对象。 在该对象之下是一个包含位置参数的可迭代对象和一个待调用的可调用对象。
BUILD_MAP_UNPACK_WITH_CALL
和BUILD_TUPLE_UNPACK_WITH_CALL
可用于合并多个映射对象和包含参数的可迭代对象。 在该可调用对象被调用之前,映射对象和可迭代对象会被分别“解包”并将它们的内容分别作为关键字参数和位置参数传入。CALL_FUNCTION_EX
会从栈中弹出所有参数以及可调用对象,附带这些参数调用该可调用对象,并将可调用对象所返回的返回值推入栈顶。
3.6 新版功能.
LOADMETHOD
(_namei)- 从 TOS 对象加载一个名为
co_names[namei]
的方法。 TOS 将被弹出,并且当解释器可以直接调用未绑定方法时,方法和 TOS 会被推入栈顶。 TOS 将被用作CALL_METHOD
的第一个参数 (self
)。 否则,NULL
和方法会被推入栈顶(方法是绑定方法或其他对象)。
3.7 新版功能.
CALLMETHOD
(_argc)- 调用一个方法。 argc 是位置参数的数量。 不支持关键字参数。 此操作码被设计用于配合
LOAD_METHOD
使用。 位置参数放在栈顶。 在它们之下放在栈中的是由LOAD_METHOD
所描述的两个条目。 它们会被全部弹出并将返回值推入栈顶。
3.7 新版功能.
MAKEFUNCTION
(_argc)将一个新函数对象推入栈顶。 从底端到顶端,如果参数带有指定的旗标值则所使用的栈必须由这些值组成。
0x01
是一个默认值的元组,用于按位置排序的仅限位置形参以及位置或关键字形参0x02
是一个仅限关键字形参的默认值的字典0x04
是一个标注字典0x08
一个包含用于自由变量的单元的元组,生成一个闭包与函数 (在 TOS1) 相关联的代码
函数的 qualified name (在 TOS)
BUILDSLICE
(_argc)- 将一个切片对象推入栈顶。 argc 必须为 2 或 3。 如果为 2,则推入
slice(TOS1, TOS)
;如果为 3,则推入slice(TOS2, TOS1, TOS)
。 请参阅slice()
内置函数了解详细信息。
EXTENDEDARG
(_ext)- 为任意带有大到无法放入默认的单字节的参数的操作码添加前缀。 ext 存放一个附加字节作为参数中的高比特位。 对于每个操作码,最多允许三个
EXTENDED_ARG
前缀,构成两字节到三字节的参数。
使用 PyObject_Format()
执行格式化。 结果会被推入栈顶。
3.6 新版功能.
在 3.6 版更改: 现在每条指令都带有参数,但操作码 < HAVE_ARGUMENT
会忽略它。 之前仅限操作码 >= HAVE_ARGUMENT
带有参数。
操作码集合
提供这些集合用于字节码指令的自动内省: