32.12. dis
— Python字节码的反汇编¶
源代码: Lib/dis.py
dis
模块支持通过反汇编分析CPython bytecode。该模块采用的CPython字节码作为输入在文件 Include/opcode.h
中定义,并由编译器和解释器使用。
字节码是CPython解释器的实现细节。不保证字节码不会在Python版本之间添加,删除或更改。使用此模块不应被视为跨越Python VM或Python版本。
示例:给定函数 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”是行号)。
32.12.1. 字节码分析¶
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()
将针对指定的操作码显示“当前指令”标记。-
codeobj
¶ 编译的代码对象。
-
first_line
¶ 代码对象的第一个源代码行(如果可用)
-
info
()¶ 返回格式化的多行字符串,其中包含有关代码对象的详细信息,如
code_info()
。
-
例:
>>> bytecode = dis.Bytecode(myfunc)
>>> for instr in bytecode:
... print(instr.opname)
...
LOAD_GLOBAL
LOAD_FAST
CALL_FUNCTION
RETURN_VALUE
32.12.2. 分析功能¶
dis
模块还定义了将输入直接转换为所需输出的以下分析功能。如果只执行单个操作,它们可能很有用,因此中间分析对象不起作用:
-
dis.
code_info
(x)¶ 返回带有提供的函数,生成器,方法,源代码字符串或代码对象的详细代码对象信息的格式化多行字符串。
注意,代码信息字符串的确切内容高度依赖于实现,并且它们可以在Python VM或Python版本中任意改变。
3.2 新版功能.
-
dis.
show_code
(x, *, file=None)¶ 将提供的函数,方法,源代码字符串或代码对象的详细代码对象信息打印到 file (如果未指定 file,则为
sys.stdout
)。这是
print(code_info(x), file=file)
的一个方便的简写,用于在解释器提示下进行交互式探索。3.2 新版功能.
在 3.4 版更改: 添加了 file 参数。
-
dis.
dis
(x=None, *, file=None)¶ 拆卸 x 对象。 x 可以表示模块,类,方法,函数,生成器,代码对象,源代码字符串或原始字节码的字节序列。对于模块,它会拆散所有函数。对于一个类,它反汇编所有的方法(包括类和静态方法)。对于代码对象或原始字节码序列,每个字节码指令打印一行。字符串首先被编译为使用
compile()
内置函数代码对象,然后被反汇编。如果没有提供对象,则此函数将反汇编最后一个回溯。反汇编作为文本写入提供的 file 参数(如果提供),否则写入
sys.stdout
。在 3.4 版更改: 添加了 file 参数。
-
dis.
distb
(tb=None, *, file=None)¶ 如果没有通过,使用最后一个traceback来反汇编回栈顶层函数。指示引起异常的指令。
反汇编作为文本写入提供的 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 参数。
-
dis.
get_instructions
(x, *, first_line=None)¶ 返回迭代器在提供的函数,方法,源代码字符串或代码对象的指令。
迭代器生成一系列
Instruction
命名元组,给出所提供代码中每个操作的详细信息。如果 first_line 不是
None
,则它指示应该为拆卸的代码中的第一源行报告的行号。否则,源行信息(如果有的话)直接取自已拆解的代码对象。3.4 新版功能.
-
dis.
findlinestarts
(code)¶ 此生成函数使用代码对象 code 的
co_firstlineno
和co_lnotab
属性来查找作为源代码中行开始的偏移。它们生成为(offset, lineno)
对。
-
dis.
findlabels
(code)¶ 检测作为跳转目标的代码对象 code 中的所有偏移量,并返回这些偏移量的列表。
-
dis.
stack_effect
(opcode[, oparg])¶ 用参数 oparg 计算 opcode 的堆栈效应。
3.4 新版功能.
32.12.3. Python字节码说明¶
get_instructions()
函数和 Bytecode
类提供了作为 Instruction
实例的字节码指令的详细信息:
-
class
dis.
Instruction
¶ 字节码操作的详细信息
-
opname
¶ 人类可读的名字操作
-
arg
¶ 操作的数字参数(如果有),否则为
None
-
argval
¶ 解析的arg值(如果已知),否则与arg相同
-
argrepr
¶ 操作参数的人工可读描述
-
offset
¶ 在字节码序列中启动操作的索引
-
starts_line
¶ 行由此操作码开始(如果有),否则为
None
-
is_jump_target
¶ True
如果其他代码跳到这里,否则False
3.4 新版功能.
-
Python编译器当前生成以下字节码指令。
一般说明
-
NOP
¶ 不做任何代码。由字节码优化器用作占位符。
-
POP_TOP
¶ 删除顶部(TOS)项目。
-
ROT_TWO
¶ 交换两个最顶层的堆栈项。
-
ROT_THREE
¶ 将第二和第三堆叠物品提升一个位置,将顶部向下移动到位置三。
-
DUP_TOP
¶ 复制堆栈顶部的引用。
-
DUP_TOP_TWO
¶ 复制堆栈顶部的两个引用,使它们按相同的顺序。
一元操作
一元操作占用堆栈的顶部,应用操作,并将结果推回堆栈。
-
UNARY_POSITIVE
¶ 实施
TOS = +TOS
。
-
UNARY_NEGATIVE
¶ 实施
TOS = -TOS
。
-
UNARY_NOT
¶ 实施
TOS = not TOS
。
-
UNARY_INVERT
¶ 实施
TOS = ~TOS
。
-
GET_ITER
¶ 实施
TOS = iter(TOS)
。
-
GET_YIELD_FROM_ITER
¶ 如果
TOS
是 generator iterator 或 coroutine 对象,则保留原样。否则,实现TOS = iter(TOS)
。3.5 新版功能.
二进制操作
二进制操作从堆栈中删除堆栈的顶部(TOS)和第二个最顶层的堆栈项目(TOS1)。他们执行操作,并将结果放回堆栈。
-
BINARY_POWER
¶ 实施
TOS = TOS1 ** TOS
。
-
BINARY_MULTIPLY
¶ 实施
TOS = TOS1 * TOS
。
-
BINARY_MATRIX_MULTIPLY
¶ 实施
TOS = TOS1 @ TOS
。3.5 新版功能.
-
BINARY_FLOOR_DIVIDE
¶ 实施
TOS = TOS1 // TOS
。
-
BINARY_TRUE_DIVIDE
¶ 实施
TOS = TOS1 / TOS
。
-
BINARY_MODULO
¶ 实施
TOS = TOS1 % TOS
。
-
BINARY_ADD
¶ 实施
TOS = TOS1 + TOS
。
-
BINARY_SUBTRACT
¶ 实施
TOS = TOS1 - TOS
。
-
BINARY_SUBSCR
¶ 实施
TOS = TOS1[TOS]
。
-
BINARY_LSHIFT
¶ 实施
TOS = TOS1 << TOS
。
-
BINARY_RSHIFT
¶ 实施
TOS = TOS1 >> TOS
。
-
BINARY_AND
¶ 实施
TOS = TOS1 & TOS
。
-
BINARY_XOR
¶ 实施
TOS = TOS1 ^ TOS
。
-
BINARY_OR
¶ 实施
TOS = TOS1 | TOS
。
就地操作
就地操作类似于二进制操作,因为它们删除TOS和TOS1,并将结果推回堆栈,但是操作是在TOS1支持它时就地完成的,并且生成的TOS可以是(但不是是)原来的TOS1。
-
INPLACE_POWER
¶ 实现就地
TOS = TOS1 ** TOS
。
-
INPLACE_MULTIPLY
¶ 实现就地
TOS = TOS1 * TOS
。
-
INPLACE_MATRIX_MULTIPLY
¶ 实现就地
TOS = TOS1 @ TOS
。3.5 新版功能.
-
INPLACE_FLOOR_DIVIDE
¶ 实现就地
TOS = TOS1 // TOS
。
-
INPLACE_TRUE_DIVIDE
¶ 实现就地
TOS = TOS1 / TOS
。
-
INPLACE_MODULO
¶ 实现就地
TOS = TOS1 % TOS
。
-
INPLACE_ADD
¶ 实现就地
TOS = TOS1 + TOS
。
-
INPLACE_SUBTRACT
¶ 实现就地
TOS = TOS1 - TOS
。
-
INPLACE_LSHIFT
¶ 实现就地
TOS = TOS1 << TOS
。
-
INPLACE_RSHIFT
¶ 实现就地
TOS = TOS1 >> TOS
。
-
INPLACE_AND
¶ 实现就地
TOS = TOS1 & TOS
。
-
INPLACE_XOR
¶ 实现就地
TOS = TOS1 ^ TOS
。
-
INPLACE_OR
¶ 实现就地
TOS = TOS1 | TOS
。
-
STORE_SUBSCR
¶ 实施
TOS1[TOS] = TOS2
。
-
DELETE_SUBSCR
¶ 实施
del TOS1[TOS]
。
协程操作码
-
GET_AWAITABLE
¶ 实现
TOS = get_awaitable(TOS)
,其中get_awaitable(o)
返回o
,如果o
是协程对象或具有CO_ITERABLE_COROUTINE标志的生成器对象,或者解析o.__await__
。
-
GET_AITER
¶ 实施
TOS = get_awaitable(TOS.__aiter__())
。有关get_awaitable
的详细信息,请参阅GET_AWAITABLE
-
GET_ANEXT
¶ 实施
PUSH(get_awaitable(TOS.__anext__()))
。有关get_awaitable
的详细信息,请参阅GET_AWAITABLE
-
BEFORE_ASYNC_WITH
¶ 从堆栈顶部的对象解析
__aenter__
和__aexit__
。将__aexit__
和__aenter__()
的结果推送到堆栈。
-
SETUP_ASYNC_WITH
¶ 创建一个新的框架对象。
其他操作码
-
SET_ADD
(i)¶ 调用
set.add(TOS1[-i], TOS)
。用于实现集合理解。
-
LIST_APPEND
(i)¶ 调用
list.append(TOS[-i], TOS)
。用于实现列表推导。
-
MAP_ADD
(i)¶ 调用
dict.setitem(TOS1[-i], TOS, TOS1)
。用于实现dict的解析。
对于所有 SET_ADD
,LIST_APPEND
和 MAP_ADD
指令,当添加的值或键/值对被弹出时,容器对象保留在堆栈上,以便它可用于循环的进一步迭代。
-
RETURN_VALUE
¶ 返回带有TOS的函数的调用者。
-
SETUP_ANNOTATIONS
¶ 检查
__annotations__
是否在locals()
中定义,如果未定义,则将其设置为空dict
。只有当类或模块体静态包含 变量注释 时,才会发出此操作码。3.6 新版功能.
-
IMPORT_STAR
¶ 将所有不以
'_'
开头的符号直接从模块TOS加载到本地命名空间。加载所有名称后,将弹出该模块。这个操作码实现from module import *
。
-
POP_BLOCK
¶ 从块堆栈中删除一个块。每帧,有一堆块,表示嵌套循环,try语句等。
-
POP_EXCEPT
¶ 从块堆栈中删除一个块。弹出的块必须是异常处理程序块,这是在输入except处理程序时隐式创建的。除了从帧堆栈弹出无关的值之外,最后三个弹出的值还用于恢复异常状态。
-
LOAD_BUILD_CLASS
¶ 将
builtins.__build_class__()
推送到堆栈。它后来被CALL_FUNCTION
调用来构造一个类。
-
SETUP_WITH
(delta)¶ 这个操作码在块开始之前执行几个操作。首先,它从上下文管理器加载
__exit__()
并将其推送到堆栈以供以后由WITH_CLEANUP
使用。然后,调用__enter__()
,并且推动指向 delta 的finally块。最后,调用enter方法的结果被推入堆栈。下一个操作码将忽略它(POP_TOP
),或将其存储在(a)变量(STORE_FAST
,STORE_NAME
或UNPACK_SEQUENCE
)中。
-
WITH_CLEANUP_START
¶ 当
with
语句块退出时清理堆栈。 TOS是上下文管理器的__exit__()
绑定方法。 TOS下面是1-3个值,表示如何/为什么输入finally子句:SECOND =
None
(SECOND,THIRD)=(
WHY_{RETURN,CONTINUE}
),retvalSECOND =
WHY_*
;没有retval下面(SECOND,THIRD,FOURTH)= exc_info()
在最后一种情况下,
TOS(SECOND, THIRD, FOURTH)
被调用,否则为TOS(None, None, None)
。将SECOND和调用的结果推送到堆栈。
-
WITH_CLEANUP_FINISH
¶ 从堆栈中弹出“exit”函数调用的异常类型和结果。
如果堆栈表示一个异常,and 的函数调用返回一个’true’值,这个信息被“zapped”,并替换为单个
WHY_SILENCED
,以防止END_FINALLY
重新提高异常。 (但非本地gotos将仍然恢复。)
所有以下操作码使用其参数。
-
STORE_NAME
(namei)¶ 实施
name = TOS
。 namei 是代码对象的属性co_names
中的 name 的索引。如果可能,编译器尝试使用STORE_FAST
或STORE_GLOBAL
。
-
DELETE_NAME
(namei)¶ 实现
del name
,其中 namei 是代码对象的co_names
属性的索引。
-
UNPACK_SEQUENCE
(count)¶ 将TOS解包到 count 个别值中,这些值从右到左放在堆栈中。
-
UNPACK_EX
(counts)¶ 使用已加星标的目标实现分配:将TOS中的迭代器解包为单个值,其中值的总数可以小于可迭代项中的项数:其中一个新值将是所有剩余项的列表。
counts 的低字节是列表值之前的值的数量,counts 的高字节是它之后的值的数量。结果值从右到左放在堆栈中。
-
STORE_ATTR
(namei)¶ 实现
TOS.name = TOS1
,其中 namei 是co_names
中的名称索引。
-
DELETE_ATTR
(namei)¶ 实施
del TOS.name
,使用 namei 作为co_names
的索引。
-
STORE_GLOBAL
(namei)¶ 作为
STORE_NAME
,但存储名称作为全局。
-
DELETE_GLOBAL
(namei)¶ 作为
DELETE_NAME
,但删除全局名称。
-
LOAD_CONST
(consti)¶ 将
co_consts[consti]
推送到堆栈。
-
LOAD_NAME
(namei)¶ 将与
co_names[namei]
关联的值推送到堆栈。
-
BUILD_TUPLE
(count)¶ 从堆栈中创建一个消耗元组的 count 项,并将生成的元组推送到堆栈。
-
BUILD_LIST
(count)¶ 作为
BUILD_TUPLE
,但创建一个列表。
-
BUILD_SET
(count)¶ 作为
BUILD_TUPLE
,但创建一个集合。
-
BUILD_MAP
(count)¶ 将新的字典对象推送到堆栈。字典预先设置为保存 count 条目。
-
BUILD_STRING
(count)¶ 从堆栈连接 count 字符串并将生成的字符串推送到堆栈。
3.6 新版功能.
-
LOAD_ATTR
(namei)¶ 用
getattr(TOS, co_names[namei])
替换TOS。
-
COMPARE_OP
(opname)¶ 执行布尔运算。操作名称可以在
cmp_op[opname]
中找到。
-
IMPORT_NAME
(namei)¶ 导入模块
co_names[namei]
。 TOS和TOS1被弹出并提供__import__()
的 fromlist 和 level 参数。模块对象被推入堆栈。当前命名空间不受影响:对于正确的import语句,后续的STORE_FAST
指令修改命名空间。
-
IMPORT_FROM
(namei)¶ 从TOS中找到的模块加载属性
co_names[namei]
。所得到的对象被推到堆栈上,随后由STORE_FAST
指令存储。
-
JUMP_FORWARD
(delta)¶ 通过 delta 增加字节码计数器。
-
POP_JUMP_IF_TRUE
(target)¶ 如果TOS为真,则将字节码计数器设置为 target。 TOS弹出。
-
POP_JUMP_IF_FALSE
(target)¶ 如果TOS为假,将字节码计数器设置为 target。 TOS弹出。
-
JUMP_IF_TRUE_OR_POP
(target)¶ 如果TOS为真,将字节码计数器设置为 target,并在堆栈上留下TOS。否则(TOS为假),TOS被弹出。
-
JUMP_IF_FALSE_OR_POP
(target)¶ 如果TOS为假,将字节码计数器设置为 target,并在堆栈上留下TOS。否则(TOS为真),TOS被弹出。
-
JUMP_ABSOLUTE
(target)¶ 将字节码计数器设置为 target。
-
FOR_ITER
(delta)¶ TOS是 iterator。调用其
__next__()
方法。如果这产生一个新的值,推它在堆栈(留下下面的迭代器)。如果迭代器指示它被耗尽,则TOS被弹出,并且字节代码计数器递增 delta。
-
LOAD_GLOBAL
(namei)¶ 将全局命名的
co_names[namei]
加载到堆栈。
-
SETUP_LOOP
(delta)¶ 将一个循环的块推送到块堆栈。该块跨越当前指令,大小为 delta 字节。
-
SETUP_EXCEPT
(delta)¶ 将try块从try-except子句推送到块堆栈。 delta 指向第一个除外块。
-
SETUP_FINALLY
(delta)¶ 将try块从try-except子句推送到块堆栈。 delta 指向finally块。
-
LOAD_FAST
(var_num)¶ 将对本地
co_varnames[var_num]
的引用推送到堆栈。
-
STORE_FAST
(var_num)¶ 将TOS存储到本地
co_varnames[var_num]
中。
-
DELETE_FAST
(var_num)¶ 删除本地
co_varnames[var_num]
。
-
STORE_ANNOTATION
(namei)¶ 将TOS存储为
locals()['__annotations__'][co_names[namei]] = TOS
。3.6 新版功能.
-
LOAD_CLOSURE
(i)¶ 推送对包含在单元的插槽 i 中的单元格和自由变量存储的引用。如果 i 小于 co_cellvars 的长度,则变量的名称为
co_cellvars[i]
。否则为co_freevars[i - len(co_cellvars)]
。
-
LOAD_DEREF
(i)¶ 加载单元格的插槽 i 中包含的单元格和自由变量存储。推动对单元格在堆栈上包含的对象的引用。
-
LOAD_CLASSDEREF
(i)¶ 很像
LOAD_DEREF
,但首先检查本地字典,然后咨询单元格。这用于在类主体中加载自由变量。
-
STORE_DEREF
(i)¶ 将TOS存储到包含在单元的插槽 i 中的单元和自由变量存储器中。
-
RAISE_VARARGS
(argc)¶ 引发异常。 argc 表示raise语句的参数数,范围从0到3.处理程序将找到跟踪为TOS2,参数为TOS1,异常为TOS。
-
CALL_FUNCTION
(argc)¶ 调用函数。 argc 的低字节表示位置参数的数量,高字节表示关键字参数的数量。在堆栈上,操作码首先找到关键字参数。对于每个关键字参数,值位于键的顶部。在关键字参数下面,位置参数在堆栈上,最右边的参数在顶部。在参数下面,要调用的函数对象在堆栈上。弹出所有函数参数,函数本身关闭堆栈,并推送返回值。
-
MAKE_FUNCTION
(argc)¶ 在堆栈上推送一个新的函数对象。从底部到顶部,如果参数携带指定的标志值,则使用的堆栈必须由值组成
0x01
是位置顺序中的默认参数对象的一个元组0x02
一个关键字参数的默认值的字典0x04
注释字典0x08
一个包含自由变量单元格的元组,构成一个闭包与功能相关的代码(在TOS1处)
该函数的 qualified name (在TOS处)
-
BUILD_SLICE
(argc)¶ 在堆栈上推动一个切片对象。 argc 必须为2或3.如果为2,则推送
slice(TOS1, TOS)
;如果是3,则推送slice(TOS2, TOS1, TOS)
。有关详细信息,请参阅slice()
内置函数。
-
EXTENDED_ARG
(ext)¶ 前缀任何操作码,其参数太大,不适合默认的两个字节。 ext 保存两个附加字节,它们与后续操作码的参数一起包括四字节参数,ext 是两个最高有效字节。
-
CALL_FUNCTION_VAR
(argc)¶ 调用函数。 argc 在
CALL_FUNCTION
中解释。堆栈上的顶层元素包含变量参数列表,后跟关键字和位置参数。
-
CALL_FUNCTION_KW
(argc)¶ 调用函数。 argc 在
CALL_FUNCTION
中解释。栈上的顶层元素包含关键字arguments字典,后面是显式的关键字和位置参数。
-
CALL_FUNCTION_VAR_KW
(argc)¶ 调用函数。 argc 在
CALL_FUNCTION
中解释。栈上的顶层元素包含关键字arguments字典,后面跟着variable-arguments tuple,后面跟着显式关键字和位置参数。
-
FORMAT_VALUE
(flags)¶ 用于实现格式化的字符串(f字符串)。从堆栈中弹出可选的 fmt_spec,然后是必需的 value。 flags 解释如下:
(flags & 0x03) == 0x00
:value 按原样格式化。(flags & 0x03) == 0x01
:在格式化之前在 value 上调用str()
。(flags & 0x03) == 0x02
:在格式化之前在 value 上调用repr()
。(flags & 0x03) == 0x03
:在格式化之前在 value 上调用ascii()
。(flags & 0x04) == 0x04
:从堆栈弹出 fmt_spec 并使用它,否则使用空的 fmt_spec。
格式化使用
PyObject_Format()
执行。结果被推到堆栈上。3.6 新版功能.
-
HAVE_ARGUMENT
¶ 这不是一个真正的操作码。它标识不接受参数
< HAVE_ARGUMENT
的操作码和执行>= HAVE_ARGUMENT
的操作码之间的分界线。
32.12.4. 操作码集合¶
这些集合用于自动内省字节码指令:
-
dis.
opname
¶ 操作名称序列,可使用字节码进行索引。
-
dis.
opmap
¶ 字典映射操作名称到字节码。
-
dis.
cmp_op
¶ 所有比较操作名称的顺序。
-
dis.
hasconst
¶ 具有常量参数的字节码序列。
-
dis.
hasfree
¶ 访问自由变量的字节码序列(请注意,在此上下文中的“自由”是指内部作用域引用的当前作用域中的名称或从此作用域引用的外部作用域中的名称) not 包括对全局或内置示波器)。
-
dis.
hasname
¶ 按名称访问属性的字节码序列。
-
dis.
hasjrel
¶ 具有相对跳转目标的字节码序列。
-
dis.
hasjabs
¶ 具有绝对跳转目标的字节码序列。
-
dis.
haslocal
¶ 访问局部变量的字节码序列。
-
dis.
hascompare
¶ 布尔运算的字节码序列。