kilim在JVM上实现了协程,其实现看起来挺容易的:http://www.malhar.net/sriram/kilim/thread_of_ones_own.pdf
在cPython上是否能够复制其技法呢?粗看上去,是很容易的,甚至比JVM更好实现:
- 利用sys._getframe(0)可以获得call stack上的任意frame
- frame的f_locals可以获得这个frame的所有局部变量的值(https://docs.python.org/3/library/inspect.html)
- 虽然python不支持goto或者longjmp,然后cPython的bytecode是支持JUMP_ABSOLUTE的(https://docs.python.org/3/library/dis.html)
一个更加牛B的事情是,有人已经用@goto装饰器的方式在Python上实现了GOTO了
这个是Python 2版本的:http://code.activestate.com/recipes/576944-the-goto-decorator/
这个是Python 3版本的:https://github.com/cdjc/goto
这两个实现很好的展示了如何在Python中做类似JVM上ASM库做的事情,字节码增强。
===============
但是,cPython的VM对于frame状态的建模使得在cPython上实现kilim,比JVM要难得多。在cPython某个指令执行的那一刻,有五个部分的状态
- builtins
- globals
- locals
- value_stack
- block_stack
前三个都是python里做动态执行的常客,没啥困难的。而且frame通过inpsect可以很轻易的获得locals的值。而后两者是非常困难的,我们可以来看一段我随便写的代码
for i in range(1):
print(i)
生成的bytecode是
0 SETUP_LOOP 30 (to 33)
3 LOAD_GLOBAL 0 (0)
6 LOAD_CONST 1 (1)
9 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
12 GET_ITER
>> 13 FOR_ITER 16 (to 32)
16 STORE_FAST 0 (0)
19 LOAD_GLOBAL 1 (1)
22 LOAD_FAST 0 (0)
25 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
28 POP_TOP
29 JUMP_ABSOLUTE 13
>> 32 POP_BLOCK
>> 33 LOAD_CONST 0 (0)
36 RETURN_VALUE
我们可以看到一个简单的循环,产生了一对SETUP_LOOP和POP_BLOCK,这个是操作block_stack的。以及GET_ITER和FOR_ITER这是操作value_stack的。如果我们想要直接跳入到print(i)这一行,那么就要恢复当时的block_stack,以及当时的value_stack。而且要特别注意GET_ITER的含义是把栈顶的值标识为ITER,使得FOR_ITER可以使用,所以还不是简单地把value_stack恢复就可以了
GET_ITER
Implements TOS = iter(TOS).
这篇博客把python的frame内部状态讲得非常清楚:http://tech.blog.aknin.name/tag/block-stack/
以上可以解释为什么没有人在python里搞字节码的trick了,因为这个VM太变态。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。