Executing sliced Python byte codes sometimes results in "SystemError: unknown opcode"
Executing sliced Python byte codes sometimes results in "SystemError: unknown opcode"
给定一个由以下 3 行代码编译而成的代码对象:
code = compile('''a = 1 / 0 # bad stuff. avoid running this!
b = 'good stuff'
c = True''', '', 'exec')
哪个调用 dis.dis(code)
会反汇编成:
1 0 LOAD_CONST 0 (1)
2 LOAD_CONST 1 (0)
4 BINARY_TRUE_DIVIDE
6 STORE_NAME 0 (a)
2 8 LOAD_CONST 2 ('good stuff')
10 STORE_NAME 1 (b)
3 12 LOAD_CONST 3 (True)
14 STORE_NAME 2 (c)
16 LOAD_CONST 4 (None)
18 RETURN_VALUE
如何提取和 运行 第二行的字节码,b = 'good stuff'
?
例如,如果我想提取 运行 最后一行 c = True
的字节码,它从字节索引 12
开始,我可以将代码对象的 co_code
属性,其中包含原始字节码,从索引 12
构造一个 types.CodeType
对象,然后用它调用 exec
:
import types
code3 = types.CodeType(
code.co_argcount,
code.co_kwonlyargcount,
code.co_nlocals,
code.co_stacksize,
code.co_flags,
code.co_code[12:],
code.co_consts,
code.co_names,
code.co_varnames,
code.co_filename,
code.co_name,
code.co_firstlineno,
code.co_lnotab,
code.co_freevars,
code.co_cellvars)
exec(code3)
print(eval('c'))
以便它正确输出分配给 c
的值:
True
但是,如果我尝试提取和 运行 只是第二行 b = 'good stuff'
的字节码,其范围从索引 8
到 12
(不是包括 12
):
code2 = types.CodeType(
code.co_argcount,
code.co_kwonlyargcount,
code.co_nlocals,
code.co_stacksize,
code.co_flags,
code.co_code[8:12],
code.co_consts,
code.co_names,
code.co_varnames,
code.co_filename,
code.co_name,
code.co_firstlineno,
code.co_lnotab,
code.co_freevars,
code.co_cellvars)
exec(code2)
print(eval('b'))
它产生:
XXX lineno: 1, opcode: 0
Traceback (most recent call last):
File "/path/file.py", line 21, in <module>
exec(code2)
File "", line 1, in <module>
SystemError: unknown opcode
调用 dis.dis(code2)
将显示新代码对象似乎包含 b = 'good stuff'
:
的正确字节代码
1 0 LOAD_CONST 2 ('good stuff')
2 STORE_NAME 1 (b)
所以我错过了什么?
我正在回答我自己的问题,因为我找不到关于这个主题的文档,我花了一段时间才弄清楚我遗漏了什么,所以它可能会让碰巧遇到同样问题的其他人受益。
事实证明,每个代码块都需要到return一个值——不return一个值不是一个选项。如果没有明确的 return
语句,那么 None
将被隐式 returned,正如问题中显示的最后两个字节代码所证明的那样:
16 LOAD_CONST 4 (None)
18 RETURN_VALUE
因此,通过从索引 12
中为 c = True
的最后一行切片字节码,我无意中包含了 None
的尾随隐式 return,幸运地满足了代码块要求return一个值。
当我尝试将 b = 'good stuff'
的第二行的字节码从索引 8
切片到 12
时,情况并非如此,因为它遗漏了最后两个字节代码为 return None
,从而导致 SystemError: unknown opcode
异常。
所以要解决这个问题,只需要附加最后两个字节码(实际上总共 4 个字节,因为字节码实际上已经变成了 "word" Python 中的代码 3 ) 到切片:
code2 = types.CodeType(
code.co_argcount,
code.co_kwonlyargcount,
code.co_nlocals,
code.co_stacksize,
code.co_flags,
code.co_code[8:12] + code.co_code[-4:],
code.co_consts,
code.co_names,
code.co_varnames,
code.co_filename,
code.co_name,
code.co_firstlineno,
code.co_lnotab,
code.co_freevars,
code.co_cellvars)
exec(code2)
print(eval('b'))
这将正确输出:
good stuff
给定一个由以下 3 行代码编译而成的代码对象:
code = compile('''a = 1 / 0 # bad stuff. avoid running this!
b = 'good stuff'
c = True''', '', 'exec')
哪个调用 dis.dis(code)
会反汇编成:
1 0 LOAD_CONST 0 (1)
2 LOAD_CONST 1 (0)
4 BINARY_TRUE_DIVIDE
6 STORE_NAME 0 (a)
2 8 LOAD_CONST 2 ('good stuff')
10 STORE_NAME 1 (b)
3 12 LOAD_CONST 3 (True)
14 STORE_NAME 2 (c)
16 LOAD_CONST 4 (None)
18 RETURN_VALUE
如何提取和 运行 第二行的字节码,b = 'good stuff'
?
例如,如果我想提取 运行 最后一行 c = True
的字节码,它从字节索引 12
开始,我可以将代码对象的 co_code
属性,其中包含原始字节码,从索引 12
构造一个 types.CodeType
对象,然后用它调用 exec
:
import types
code3 = types.CodeType(
code.co_argcount,
code.co_kwonlyargcount,
code.co_nlocals,
code.co_stacksize,
code.co_flags,
code.co_code[12:],
code.co_consts,
code.co_names,
code.co_varnames,
code.co_filename,
code.co_name,
code.co_firstlineno,
code.co_lnotab,
code.co_freevars,
code.co_cellvars)
exec(code3)
print(eval('c'))
以便它正确输出分配给 c
的值:
True
但是,如果我尝试提取和 运行 只是第二行 b = 'good stuff'
的字节码,其范围从索引 8
到 12
(不是包括 12
):
code2 = types.CodeType(
code.co_argcount,
code.co_kwonlyargcount,
code.co_nlocals,
code.co_stacksize,
code.co_flags,
code.co_code[8:12],
code.co_consts,
code.co_names,
code.co_varnames,
code.co_filename,
code.co_name,
code.co_firstlineno,
code.co_lnotab,
code.co_freevars,
code.co_cellvars)
exec(code2)
print(eval('b'))
它产生:
XXX lineno: 1, opcode: 0
Traceback (most recent call last):
File "/path/file.py", line 21, in <module>
exec(code2)
File "", line 1, in <module>
SystemError: unknown opcode
调用 dis.dis(code2)
将显示新代码对象似乎包含 b = 'good stuff'
:
1 0 LOAD_CONST 2 ('good stuff')
2 STORE_NAME 1 (b)
所以我错过了什么?
我正在回答我自己的问题,因为我找不到关于这个主题的文档,我花了一段时间才弄清楚我遗漏了什么,所以它可能会让碰巧遇到同样问题的其他人受益。
事实证明,每个代码块都需要到return一个值——不return一个值不是一个选项。如果没有明确的 return
语句,那么 None
将被隐式 returned,正如问题中显示的最后两个字节代码所证明的那样:
16 LOAD_CONST 4 (None)
18 RETURN_VALUE
因此,通过从索引 12
中为 c = True
的最后一行切片字节码,我无意中包含了 None
的尾随隐式 return,幸运地满足了代码块要求return一个值。
当我尝试将 b = 'good stuff'
的第二行的字节码从索引 8
切片到 12
时,情况并非如此,因为它遗漏了最后两个字节代码为 return None
,从而导致 SystemError: unknown opcode
异常。
所以要解决这个问题,只需要附加最后两个字节码(实际上总共 4 个字节,因为字节码实际上已经变成了 "word" Python 中的代码 3 ) 到切片:
code2 = types.CodeType(
code.co_argcount,
code.co_kwonlyargcount,
code.co_nlocals,
code.co_stacksize,
code.co_flags,
code.co_code[8:12] + code.co_code[-4:],
code.co_consts,
code.co_names,
code.co_varnames,
code.co_filename,
code.co_name,
code.co_firstlineno,
code.co_lnotab,
code.co_freevars,
code.co_cellvars)
exec(code2)
print(eval('b'))
这将正确输出:
good stuff