Skip to main content

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() 将针对指定的操作码显示“当前指令”标记。

classmethod from_traceback(tb)

从给定的追溯构建 Bytecode 实例,将 current_offset 设置为负责异常的指令。

codeobj

编译的代码对象。

first_line

代码对象的第一个源代码行(如果可用)

dis()

返回字节码操作的格式化视图(与 dis.dis() 打印的相同,但返回为多行字符串)。

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,则指示最后一条指令。输出分为以下列:

  1. 行号,用于每行的第一条指令

  2. 当前指令,表示为 -->

  3. 标记的指令,用 >> 指示,

  4. 指令的地址,

  5. 操作代码名称,

  6. 操作参数和

  7. 括号中的参数的解释。

参数解释识别局部和全局变量名,常量值,分支目标和比较运算符。

反汇编作为文本写入提供的 file 参数(如果提供),否则写入 sys.stdout

在 3.4 版更改: 添加了 file 参数。

dis.get_instructions(x, *, first_line=None)

返回迭代器在提供的函数,方法,源代码字符串或代码对象的指令。

迭代器生成一系列 Instruction 命名元组,给出所提供代码中每个操作的详细信息。

如果 first_line 不是 None,则它指示应该为拆卸的代码中的第一源行报告的行号。否则,源行信息(如果有的话)直接取自已拆解的代码对象。

3.4 新版功能.

dis.findlinestarts(code)

此生成函数使用代码对象 codeco_firstlinenoco_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

字节码操作的详细信息

opcode

用于操作的数字代码,对应于下面列出的操作码值和 操作码集合 中的字节码值。

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

如果 TOSgenerator iteratorcoroutine 对象,则保留原样。否则,实现 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

创建一个新的框架对象。

其他操作码

PRINT_EXPR

实现交互式模式的表达式语句。 TOS从堆栈中删除并打印。在非交互模式下,表达式语句用 POP_TOP 终止。

BREAK_LOOP

由于 break 语句而终止循环。

CONTINUE_LOOP(target)

由于 continue 语句而继续循环。 target 是要跳转的地址(应为 FOR_ITER 指令)。

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_ADDLIST_APPENDMAP_ADD 指令,当添加的值或键/值对被弹出时,容器对象保留在堆栈上,以便它可用于循环的进一步迭代。

RETURN_VALUE

返回带有TOS的函数的调用者。

YIELD_VALUE

Pops TOS并从 generator 产生它。

YIELD_FROM

弹出TOS并委托它作为从 generator 的subiterator。

3.3 新版功能.

SETUP_ANNOTATIONS

检查 __annotations__ 是否在 locals() 中定义,如果未定义,则将其设置为空 dict。只有当类或模块体静态包含 变量注释 时,才会发出此操作码。

3.6 新版功能.

IMPORT_STAR

将所有不以 '_' 开头的符号直接从模块TOS加载到本地命名空间。加载所有名称后,将弹出该模块。这个操作码实现 from module import *

POP_BLOCK

从块堆栈中删除一个块。每帧,有一堆块,表示嵌套循环,try语句等。

POP_EXCEPT

从块堆栈中删除一个块。弹出的块必须是异常处理程序块,这是在输入except处理程序时隐式创建的。除了从帧堆栈弹出无关的值之外,最后三个弹出的值还用于恢复异常状态。

END_FINALLY

终止 finally 子句。解释器重新调用异常是否必须重新提高,或者该函数是否返回,并继续下一个下一个块。

LOAD_BUILD_CLASS

builtins.__build_class__() 推送到堆栈。它后来被 CALL_FUNCTION 调用来构造一个类。

SETUP_WITH(delta)

这个操作码在块开始之前执行几个操作。首先,它从上下文管理器加载 __exit__() 并将其推送到堆栈以供以后由 WITH_CLEANUP 使用。然后,调用 __enter__(),并且推动指向 delta 的finally块。最后,调用enter方法的结果被推入堆栈。下一个操作码将忽略它(POP_TOP),或将其存储在(a)变量(STORE_FASTSTORE_NAMEUNPACK_SEQUENCE)中。

WITH_CLEANUP_START

with 语句块退出时清理堆栈。 TOS是上下文管理器的 __exit__() 绑定方法。 TOS下面是1-3个值,表示如何/为什么输入finally子句:

  • SECOND = None

  • (SECOND,THIRD)=(WHY_{RETURN,CONTINUE}),retval

  • SECOND = 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 = TOSnamei 是代码对象的属性 co_names 中的 name 的索引。如果可能,编译器尝试使用 STORE_FASTSTORE_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,其中 nameico_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_CONST_KEY_MAP(count)

专用于常数键的 BUILD_MAP 版本。 count 值从堆栈消耗。栈上的顶层元素包含一个元组的键。

3.6 新版功能.

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__()fromlistlevel 参数。模块对象被推入堆栈。当前命名空间不受影响:对于正确的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 中的单元和自由变量存储器中。

DELETE_DEREF(i)

清空包含在单元的插槽 i 中的单元和自由变量存储。用于 del 语句。

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)

调用函数。 argcCALL_FUNCTION 中解释。堆栈上的顶层元素包含变量参数列表,后跟关键字和位置参数。

CALL_FUNCTION_KW(argc)

调用函数。 argcCALL_FUNCTION 中解释。栈上的顶层元素包含关键字arguments字典,后面是显式的关键字和位置参数。

CALL_FUNCTION_VAR_KW(argc)

调用函数。 argcCALL_FUNCTION 中解释。栈上的顶层元素包含关键字arguments字典,后面跟着variable-arguments tuple,后面跟着显式关键字和位置参数。

FORMAT_VALUE(flags)

用于实现格式化的字符串(f字符串)。从堆栈中弹出可选的 fmt_spec,然后是必需的 valueflags 解释如下:

  • (flags & 0x03) == 0x00value 按原样格式化。

  • (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

布尔运算的字节码序列。