32.12. dis - Python字节码的反汇编程序

源代码: Lib/dis.py

dis模块支持通过反汇编分析CPython bytecode该模块作为输入的CPython字节码在文件Include/opcode.h中定义,由编译器和解释器使用。

CPython实现细节:字节码是CPython解释器的实现细节。不保证字节码不会在Python版本之间添加,删除或更改。使用此模块不应被视为在Python VM或Python版本上工作。

示例:给定函数myfunc()

def myfunc(alist):
    return len(alist)

以下命令可用于显示myfunc()的反汇编:

>>> dis.dis(myfunc)
  2           0 LOAD_GLOBAL              0 (len)
              3 LOAD_FAST                0 (alist)
              6 CALL_FUNCTION            1
              9 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)

从给定traceback构造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)

文件(或sys.stdout)中提供的函数,方法,源代码字符串或代码对象的详细代码对象信息打印到文件指定)。

这是打印(code_info(x), file = file)的方便缩写,用于在解释器提示符处进行交互式探索。

版本3.2中的新功能。

在版本3.4中更改:添加了文件参数。

dis.dis(x=None, *, file=None)

拆分x对象。x可以表示模块,类,方法,函数,生成器,代码对象,源代码字符串或原始字节码的字节序列。对于模块,它会拆散所有函数。对于一个类,它反汇编所有的方法(包括类和静态方法)。对于代码对象或原始字节码序列,每个字节码指令打印一行。在反汇编之前,字符串首先用compile()内建函数编译为代码对象。如果没有提供对象,则此函数将反汇编最后一个回溯。

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

在版本3.4中更改:添加了文件参数。

dis.distb(tb=None, *, file=None)

如果没有通过,则使用最后一个traceback来反汇编回栈顶层函数。指示引起异常的指令。

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

在版本3.4中更改:添加了文件参数。

dis.disassemble(code, lasti=-1, *, file=None)
dis.disco(code, lasti=-1, *, file=None)

拆分代码对象,如果提供lasti,则指示最后一条指令。输出分为以下列:

  1. 行号,用于每行的第一条指令
  2. 指示为-->的当前指令,
  3. >>指示的标记指令,
  4. 指令的地址,
  5. 操作代码名称,
  6. 操作参数和
  7. 括号中的参数的解释。

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

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

在版本3.4中更改:添加了文件参数。

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

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

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

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

版本3.4中的新功能。

dis.findlinestarts(code)

这个生成器函数使用代码对象代码co_firstlinenoco_lnotab属性来找到源代码中行开头的偏移。它们生成为(偏移, lineno)对。

dis.findlabels(code)

检测作为跳转目标的代码对象代码中的所有偏移量,并返回这些偏移量的列表。

dis.stack_effect(opcode[, oparg])

计算opcode与参数oparg的堆栈效果。

版本3.4中的新功能。

32.12.3. Python字节码指令

get_instructions()函数和Bytecode类提供了字节码指令作为Instruction实例的详细信息:

class dis.Instruction

字节码操作的详细信息

opcode

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

opname

操作的人类可读名称

arg

操作的数字参数(如果有),否则为无

argval

解析的arg值(如果已知),否则与arg相同

argrepr

操作参数的可读描述

offset

在字节码序列内的操作的开始索引

starts_line

由此操作码开始的行(如果有),否则为无

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 = 而不是 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 t0 >。

BINARY_RSHIFT

实施TOS = TOS1 >> TOS t0 >。

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 t0>。

INPLACE_MULTIPLY

立即实施TOS = TOS1 * TOS t0 >。

INPLACE_MATRIX_MULTIPLY

实施TOS = TOS1 @ TOS t0 >。

版本3.5中的新功能。

INPLACE_FLOOR_DIVIDE

实施TOS = TOS1 // TOS t0>。

INPLACE_TRUE_DIVIDE

实施TOS = TOS1 / TOS t0 >。

INPLACE_MODULO

实施TOS = TOS1 TOS t0 >。

INPLACE_ADD

立即实施TOS = TOS1 + TOS t0 >。

INPLACE_SUBTRACT

实施TOS = TOS1 - TOS t0 >。

INPLACE_LSHIFT

实现TOS = TOS1 TOS

INPLACE_RSHIFT

Implements in-place TOS = TOS1 >> TOS.

INPLACE_AND

实施TOS = TOS1 & TOS t0>。

INPLACE_XOR

立即实施TOS = TOS1 ^ TOS t0 >。

INPLACE_OR

实施TOS = TOS1 | TOS t0 >。

STORE_SUBSCR

实现TOS1[TOS] = TOS2.

DELETE_SUBSCR

实现del TOS1[TOS].

协程操作码

GET_AWAITABLE

Implements TOS = get_awaitable(TOS), where get_awaitable(o) returns o if o is a coroutine object or a generator object with the CO_ITERABLE_COROUTINE flag, or resolves 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

弹出TOS并从generator生成它。

YIELD_FROM

弹出TOS并从generator中委托它作为辅助参数。

版本3.3中的新功能。

IMPORT_STAR T0> ¶ T1>

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

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方法的结果被推入堆栈。The next opcode will either ignore it (POP_TOP), or store it in (a) variable(s) (STORE_FAST, STORE_NAME, or UNPACK_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(无, 无, 无)将SECOND和调用的结果推送到堆栈。

WITH_CLEANUP_FINISH

从堆栈中弹出“exit”函数调用的异常类型和结果。

如果堆栈表示异常,函数调用返回“true”值,此信息为“zapped”,并替换为单个WHY_SILENCED,以防止END_FINALLY重新提高异常。(但非本地gotos将仍然恢复。)

所有以下操作码都需要参数。参数是两个字节,最高有效字节。

STORE_NAME(namei)

实施名称 = TOSnamei是代码对象的属性co_names名称的索引。如果可能,编译器尝试使用STORE_FASTSTORE_GLOBAL

DELETE_NAME(namei)

实现del 名称,其中nameico_names目的。

UNPACK_SEQUENCE(count)

将TOS解包到计数个别值中,这些值从右到左放在堆栈中。

UNPACK_EX(counts)

使用已加星标的目标实现分配:将TOS中的迭代包解压为单个值,其中值的总数可以小于可迭代项中的项数:其中一个新值将是所有剩余项的列表。

计数的低字节是列表值之前的值的数量,的高字节计数它之后的值的数量。结果值从右到左放在堆栈中。

STORE_ATTR(namei)

实现TOS.name = TOS1,其中namei co_names

DELETE_ATTR(namei)

使用namei作为co_names中的索引,实现del TOS.name

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)

从堆栈中创建消耗计数的元组,并将生成的元组推送到堆栈。

BUILD_LIST(count)

用作BUILD_TUPLE,但会创建列表。

BUILD_SET(count)

用作BUILD_TUPLE,但会创建一个集合。

BUILD_MAP(count)

将一个新的字典对象推送到堆栈。字典预先设置为保存计数条目。

LOAD_ATTR(namei)

getattr(TOS, co_names [namei])替换TOS。

COMPARE_OP(opname)

执行布尔操作。操作名称可以在cmp_op[opname]中找到。

IMPORT_NAME(namei)

导入模块co_names[namei]TOS和TOS1被弹出并提供__import__()从列表参数。模块对象被推入堆栈。当前命名空间不受影响:对于正确的导入语句,后续的STORE_FAST指令修改命名空间。

IMPORT_FROM(namei)

从TOS中找到的模块加载属性co_names[namei]结果对象被推入堆栈,随后通过STORE_FAST指令存储。

JUMP_FORWARD(delta)

通过delta增加字节码计数器。

POP_JUMP_IF_TRUE(target)

如果TOS为真,则将字节码计数器设置为targetTOS弹出。

POP_JUMP_IF_FALSE(target)

如果TOS为假,则将字节码计数器设置为targetTOS弹出。

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)

将字节码计数器设置为目标

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]

LOAD_CLOSURE(i)

推动对单元格的插槽i中包含的单元格的引用和自由变量存储。变量的名称为co_cellvars[i] if i小于co_cellvars的长度。否则为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指示引数语句的参数数,范围为0到3。处理程序将找到跟踪作为TOS2,参数为TOS1,异常为TOS。

CALL_FUNCTION(argc)

调用函数。argc的低字节表示位置参数的数量,高字节表示关键字参数的数量。在堆栈上,操作码首先找到关键字参数。对于每个关键字参数,值是在键的顶部。在关键字参数下面,位置参数在堆栈上,最右边的参数在顶部。在参数下面,要调用的函数对象在堆栈上。弹出所有函数参数,函数本身关闭堆栈,并推送返回值。

MAKE_FUNCTION(argc)

在堆栈上推送一个新的函数对象。从底部到顶部,消耗的堆栈必须包含

  • argc & 0xFF
  • (argc > 8) & 0xFF / t0>名称和默认参数对,其名称正好位于堆栈上的对象下方,用于仅限关键字的参数
  • (argc > 16) & 0x7FFF / t0>参数注释对象
  • 一个列出注解的参数名称的元组(仅当存在ony注释对象时)
  • 与功能相关的代码(在TOS1处)
  • 函数的qualified name(在TOS处)
MAKE_CLOSURE(argc)

创建一个新的函数对象,设置其__ closure __槽,并将其推入堆栈。TOS是函数的qualified name,TOS1是与函数相关联的代码,TOS2是包含闭包自由变量的单元格的元组。argc解释为MAKE_FUNCTION;注释和默认值也在TOS2之下的相同顺序。

BUILD_SLICE(argc)

在堆栈上推动一个切片对象。argc必须为2或3。如果为2,则按下切片(TOS1, TOS) 如果是3,则按下切片(TOS2, TOS1, TOS) 有关详细信息,请参阅slice()内建函数。

EXTENDED_ARG(ext)

前缀任何操作码,其参数太大,不适合默认的两个字节。ext包含两个附加字节,与后续操作码的参数一起构成一个四字节参数,ext是两个最高有效字节。

CALL_FUNCTION_VAR(argc)

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

CALL_FUNCTION_KW(argc)

调用函数。argc解释为CALL_FUNCTION堆栈顶部的元素包含关键字参数字典,其次是显式关键字和位置参数。

CALL_FUNCTION_VAR_KW(argc)

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

HAVE_ARGUMENT T0> ¶ T1>

这不是一个真正的操作码。它标识不接受参数的操作码之间的分界线 HAVE_ARGUMENT和执行> = HAVE_ARGUMENT

32.12.4. 操作码集合

这些容器用于自动内省字节码指令:

dis.opname

操作名称序列,使用字节码可索引。

dis.opmap

将操作名称映射到字节码的字典。

dis.cmp_op

所有比较操作名称的顺序。

dis.hasconst

具有常量参数的字节码序列。

dis.hasfree

访问自由变量的字节码序列(请注意,在本上下文中的“自由”是指内部作用域引用的当前作用域中的名称或从此作用域引用的外部作用域中的名称。包括对全局或内置作用域的引用)。

dis.hasname

按名称访问属性的字节码序列。

dis.hasjrel

具有相对跳转目标的字节码序列。

dis.hasjabs

具有绝对跳转目标的字节码序列。

dis.haslocal

访问局部变量的字节码序列。

dis.hascompare

布尔运算的字节码序列。